@ibodr/validate 0.0.0
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/dist/index.d.ts +1108 -0
- package/dist/index.mjs +1112 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +35 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,1112 @@
|
|
|
1
|
+
import { validateIndexKey, registerDrawLibraryVersion, exhaustiveSwitchError, hasOwnProperty, getOwnProperty, STRUCTURED_CLONE_OBJECT_PROTOTYPE } from '@ibodr/utils';
|
|
2
|
+
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __export = (target, all) => {
|
|
5
|
+
for (var name in all)
|
|
6
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
// src/lib/validation.ts
|
|
10
|
+
var validation_exports = {};
|
|
11
|
+
__export(validation_exports, {
|
|
12
|
+
ArrayOfValidator: () => ArrayOfValidator,
|
|
13
|
+
DictValidator: () => DictValidator,
|
|
14
|
+
ObjectValidator: () => ObjectValidator,
|
|
15
|
+
UnionValidator: () => UnionValidator,
|
|
16
|
+
ValidationError: () => ValidationError,
|
|
17
|
+
Validator: () => Validator,
|
|
18
|
+
any: () => any,
|
|
19
|
+
array: () => array,
|
|
20
|
+
arrayOf: () => arrayOf,
|
|
21
|
+
bigint: () => bigint,
|
|
22
|
+
boolean: () => boolean,
|
|
23
|
+
dict: () => dict,
|
|
24
|
+
httpUrl: () => httpUrl,
|
|
25
|
+
indexKey: () => indexKey,
|
|
26
|
+
integer: () => integer,
|
|
27
|
+
jsonDict: () => jsonDict,
|
|
28
|
+
jsonValue: () => jsonValue,
|
|
29
|
+
linkUrl: () => linkUrl,
|
|
30
|
+
literal: () => literal,
|
|
31
|
+
literalEnum: () => literalEnum,
|
|
32
|
+
model: () => model,
|
|
33
|
+
nonZeroFiniteNumber: () => nonZeroFiniteNumber,
|
|
34
|
+
nonZeroInteger: () => nonZeroInteger,
|
|
35
|
+
nonZeroNumber: () => nonZeroNumber,
|
|
36
|
+
nullable: () => nullable,
|
|
37
|
+
number: () => number,
|
|
38
|
+
numberUnion: () => numberUnion,
|
|
39
|
+
object: () => object,
|
|
40
|
+
optional: () => optional,
|
|
41
|
+
or: () => or,
|
|
42
|
+
positiveInteger: () => positiveInteger,
|
|
43
|
+
positiveNumber: () => positiveNumber,
|
|
44
|
+
setEnum: () => setEnum,
|
|
45
|
+
srcUrl: () => srcUrl,
|
|
46
|
+
string: () => string,
|
|
47
|
+
union: () => union,
|
|
48
|
+
unitInterval: () => unitInterval,
|
|
49
|
+
unknown: () => unknown,
|
|
50
|
+
unknownObject: () => unknownObject
|
|
51
|
+
});
|
|
52
|
+
function formatPath(path) {
|
|
53
|
+
if (!path.length) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
let formattedPath = "";
|
|
57
|
+
for (const item of path) {
|
|
58
|
+
if (typeof item === "number") {
|
|
59
|
+
formattedPath += `.${item}`;
|
|
60
|
+
} else if (item.startsWith("(")) {
|
|
61
|
+
if (formattedPath.endsWith(")")) {
|
|
62
|
+
formattedPath = `${formattedPath.slice(0, -1)}, ${item.slice(1)}`;
|
|
63
|
+
} else {
|
|
64
|
+
formattedPath += item;
|
|
65
|
+
}
|
|
66
|
+
} else {
|
|
67
|
+
formattedPath += `.${item}`;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
formattedPath = formattedPath.replace(/id = [^,]+, /, "").replace(/id = [^)]+/, "");
|
|
71
|
+
if (formattedPath.startsWith(".")) {
|
|
72
|
+
return formattedPath.slice(1);
|
|
73
|
+
}
|
|
74
|
+
return formattedPath;
|
|
75
|
+
}
|
|
76
|
+
var ValidationError = class extends Error {
|
|
77
|
+
/**
|
|
78
|
+
* Creates a new ValidationError with contextual information about where the error occurred.
|
|
79
|
+
*
|
|
80
|
+
* rawMessage - The raw error message without path information
|
|
81
|
+
* path - Array indicating the location in the data structure where validation failed
|
|
82
|
+
*/
|
|
83
|
+
constructor(rawMessage, path = []) {
|
|
84
|
+
const formattedPath = formatPath(path);
|
|
85
|
+
const indentedMessage = rawMessage.split("\n").map((line, i) => i === 0 ? line : ` ${line}`).join("\n");
|
|
86
|
+
super(path ? `At ${formattedPath}: ${indentedMessage}` : indentedMessage);
|
|
87
|
+
this.rawMessage = rawMessage;
|
|
88
|
+
this.path = path;
|
|
89
|
+
}
|
|
90
|
+
rawMessage;
|
|
91
|
+
path;
|
|
92
|
+
name = "ValidationError";
|
|
93
|
+
};
|
|
94
|
+
function prefixError(path, fn) {
|
|
95
|
+
try {
|
|
96
|
+
return fn();
|
|
97
|
+
} catch (err) {
|
|
98
|
+
if (err instanceof ValidationError) {
|
|
99
|
+
throw new ValidationError(err.rawMessage, [path, ...err.path]);
|
|
100
|
+
}
|
|
101
|
+
throw new ValidationError(err.toString(), [path]);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function typeToString(value) {
|
|
105
|
+
if (value === null) return "null";
|
|
106
|
+
if (Array.isArray(value)) return "an array";
|
|
107
|
+
const type = typeof value;
|
|
108
|
+
switch (type) {
|
|
109
|
+
case "bigint":
|
|
110
|
+
case "boolean":
|
|
111
|
+
case "function":
|
|
112
|
+
case "number":
|
|
113
|
+
case "string":
|
|
114
|
+
case "symbol":
|
|
115
|
+
return `a ${type}`;
|
|
116
|
+
case "object":
|
|
117
|
+
return `an ${type}`;
|
|
118
|
+
case "undefined":
|
|
119
|
+
return "undefined";
|
|
120
|
+
default:
|
|
121
|
+
exhaustiveSwitchError(type);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
var Validator = class _Validator {
|
|
125
|
+
/**
|
|
126
|
+
* Creates a new Validator instance.
|
|
127
|
+
*
|
|
128
|
+
* validationFn - Function that validates and returns a value of type T
|
|
129
|
+
* validateUsingKnownGoodVersionFn - Optional performance-optimized validation function
|
|
130
|
+
* skipSameValueCheck - Internal flag to skip dev check for validators that transform values
|
|
131
|
+
*/
|
|
132
|
+
constructor(validationFn, validateUsingKnownGoodVersionFn, skipSameValueCheck = false) {
|
|
133
|
+
this.validationFn = validationFn;
|
|
134
|
+
this.validateUsingKnownGoodVersionFn = validateUsingKnownGoodVersionFn;
|
|
135
|
+
this.skipSameValueCheck = skipSameValueCheck;
|
|
136
|
+
}
|
|
137
|
+
validationFn;
|
|
138
|
+
validateUsingKnownGoodVersionFn;
|
|
139
|
+
skipSameValueCheck;
|
|
140
|
+
/**
|
|
141
|
+
* Validates an unknown value and returns it with the correct type. The returned value is
|
|
142
|
+
* guaranteed to be referentially equal to the passed value.
|
|
143
|
+
*
|
|
144
|
+
* @param value - The unknown value to validate
|
|
145
|
+
* @returns The validated value with type T
|
|
146
|
+
* @throws ValidationError When validation fails
|
|
147
|
+
* @example
|
|
148
|
+
* ```ts
|
|
149
|
+
* import { T } from '@ibodr/validate'
|
|
150
|
+
*
|
|
151
|
+
* const name = T.string.validate("Alice") // Returns "Alice" as string
|
|
152
|
+
* const title = T.string.validate("") // Returns "" (empty strings are valid)
|
|
153
|
+
*
|
|
154
|
+
* // These will throw ValidationError:
|
|
155
|
+
* T.string.validate(123) // Expected string, got a number
|
|
156
|
+
* T.string.validate(null) // Expected string, got null
|
|
157
|
+
* T.string.validate(undefined) // Expected string, got undefined
|
|
158
|
+
* ```
|
|
159
|
+
*/
|
|
160
|
+
validate(value) {
|
|
161
|
+
const validated = this.validationFn(value);
|
|
162
|
+
if (!this.skipSameValueCheck && !Object.is(value, validated)) {
|
|
163
|
+
throw new ValidationError("Validator functions must return the same value they were passed");
|
|
164
|
+
}
|
|
165
|
+
return validated;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Performance-optimized validation using a previously validated value. If the new value
|
|
169
|
+
* is referentially equal to the known good value, returns the known good value immediately.
|
|
170
|
+
*
|
|
171
|
+
* @param knownGoodValue - A previously validated value
|
|
172
|
+
* @param newValue - The new value to validate
|
|
173
|
+
* @returns The validated value, potentially reusing the known good value
|
|
174
|
+
* @throws ValidationError When validation fails
|
|
175
|
+
* @example
|
|
176
|
+
* ```ts
|
|
177
|
+
* import { T } from '@ibodr/validate'
|
|
178
|
+
*
|
|
179
|
+
* const userValidator = T.object({
|
|
180
|
+
* name: T.string,
|
|
181
|
+
* settings: T.object({ theme: T.literalEnum('light', 'dark') })
|
|
182
|
+
* })
|
|
183
|
+
*
|
|
184
|
+
* const user = userValidator.validate({ name: "Alice", settings: { theme: "light" } })
|
|
185
|
+
*
|
|
186
|
+
* // Later, with partially changed data:
|
|
187
|
+
* const newData = { name: "Alice", settings: { theme: "dark" } }
|
|
188
|
+
* const updated = userValidator.validateUsingKnownGoodVersion(user, newData)
|
|
189
|
+
* // Only validates the changed 'theme' field for better performance
|
|
190
|
+
* ```
|
|
191
|
+
*/
|
|
192
|
+
validateUsingKnownGoodVersion(knownGoodValue, newValue) {
|
|
193
|
+
if (Object.is(knownGoodValue, newValue)) {
|
|
194
|
+
return knownGoodValue;
|
|
195
|
+
}
|
|
196
|
+
if (this.validateUsingKnownGoodVersionFn) {
|
|
197
|
+
return this.validateUsingKnownGoodVersionFn(knownGoodValue, newValue);
|
|
198
|
+
}
|
|
199
|
+
return this.validate(newValue);
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Type guard that checks if a value is valid without throwing an error.
|
|
203
|
+
*
|
|
204
|
+
* @param value - The value to check
|
|
205
|
+
* @returns True if the value is valid, false otherwise
|
|
206
|
+
* @example
|
|
207
|
+
* ```ts
|
|
208
|
+
* import { T } from '@ibodr/validate'
|
|
209
|
+
*
|
|
210
|
+
* function processUserInput(input: unknown) {
|
|
211
|
+
* if (T.string.isValid(input)) {
|
|
212
|
+
* // input is now typed as string within this block
|
|
213
|
+
* return input.toUpperCase()
|
|
214
|
+
* }
|
|
215
|
+
* if (T.number.isValid(input)) {
|
|
216
|
+
* // input is now typed as number within this block
|
|
217
|
+
* return input.toFixed(2)
|
|
218
|
+
* }
|
|
219
|
+
* throw new Error('Expected string or number')
|
|
220
|
+
* }
|
|
221
|
+
* ```
|
|
222
|
+
*/
|
|
223
|
+
isValid(value) {
|
|
224
|
+
try {
|
|
225
|
+
this.validate(value);
|
|
226
|
+
return true;
|
|
227
|
+
} catch {
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Returns a new validator that also accepts null values.
|
|
233
|
+
*
|
|
234
|
+
* @returns A new validator that accepts T or null
|
|
235
|
+
* @example
|
|
236
|
+
* ```ts
|
|
237
|
+
* import { T } from '@ibodr/validate'
|
|
238
|
+
*
|
|
239
|
+
* const assetValidator = T.object({
|
|
240
|
+
* id: T.string,
|
|
241
|
+
* name: T.string,
|
|
242
|
+
* src: T.srcUrl.nullable(), // Can be null if not loaded yet
|
|
243
|
+
* mimeType: T.string.nullable()
|
|
244
|
+
* })
|
|
245
|
+
*
|
|
246
|
+
* const asset = assetValidator.validate({
|
|
247
|
+
* id: "image-123",
|
|
248
|
+
* name: "photo.jpg",
|
|
249
|
+
* src: null, // Valid - asset not loaded yet
|
|
250
|
+
* mimeType: "image/jpeg"
|
|
251
|
+
* })
|
|
252
|
+
* ```
|
|
253
|
+
*/
|
|
254
|
+
nullable() {
|
|
255
|
+
return nullable(this);
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Returns a new validator that also accepts undefined values.
|
|
259
|
+
*
|
|
260
|
+
* @returns A new validator that accepts T or undefined
|
|
261
|
+
* @example
|
|
262
|
+
* ```ts
|
|
263
|
+
* import { T } from '@ibodr/validate'
|
|
264
|
+
*
|
|
265
|
+
* const shapeConfigValidator = T.object({
|
|
266
|
+
* type: T.literal('rectangle'),
|
|
267
|
+
* x: T.number,
|
|
268
|
+
* y: T.number,
|
|
269
|
+
* label: T.string.optional(), // Optional property
|
|
270
|
+
* metadata: T.object({ created: T.string }).optional()
|
|
271
|
+
* })
|
|
272
|
+
*
|
|
273
|
+
* // Both of these are valid:
|
|
274
|
+
* const shape1 = shapeConfigValidator.validate({ type: 'rectangle', x: 0, y: 0 })
|
|
275
|
+
* const shape2 = shapeConfigValidator.validate({
|
|
276
|
+
* type: 'rectangle', x: 0, y: 0, label: "My Shape"
|
|
277
|
+
* })
|
|
278
|
+
* ```
|
|
279
|
+
*/
|
|
280
|
+
optional() {
|
|
281
|
+
return optional(this);
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Creates a new validator by refining this validator with additional logic that can transform
|
|
285
|
+
* the validated value to a new type.
|
|
286
|
+
*
|
|
287
|
+
* @param otherValidationFn - Function that transforms/validates the value to type U
|
|
288
|
+
* @returns A new validator that validates to type U
|
|
289
|
+
* @throws ValidationError When validation or refinement fails
|
|
290
|
+
* @example
|
|
291
|
+
* ```ts
|
|
292
|
+
* import { T, ValidationError } from '@ibodr/validate'
|
|
293
|
+
*
|
|
294
|
+
* // Transform string to ensure it starts with a prefix
|
|
295
|
+
* const prefixedIdValidator = T.string.refine((id) => {
|
|
296
|
+
* return id.startsWith('shape:') ? id : `shape:${id}`
|
|
297
|
+
* })
|
|
298
|
+
*
|
|
299
|
+
* const id1 = prefixedIdValidator.validate("rectangle-123") // Returns "shape:rectangle-123"
|
|
300
|
+
* const id2 = prefixedIdValidator.validate("shape:circle-456") // Returns "shape:circle-456"
|
|
301
|
+
*
|
|
302
|
+
* // Parse and validate JSON strings
|
|
303
|
+
* const jsonValidator = T.string.refine((str) => {
|
|
304
|
+
* try {
|
|
305
|
+
* return JSON.parse(str)
|
|
306
|
+
* } catch {
|
|
307
|
+
* throw new ValidationError('Invalid JSON string')
|
|
308
|
+
* }
|
|
309
|
+
* })
|
|
310
|
+
* ```
|
|
311
|
+
*/
|
|
312
|
+
refine(otherValidationFn) {
|
|
313
|
+
return new _Validator(
|
|
314
|
+
(value) => {
|
|
315
|
+
return otherValidationFn(this.validate(value));
|
|
316
|
+
},
|
|
317
|
+
(knownGoodValue, newValue) => {
|
|
318
|
+
const validated = this.validateUsingKnownGoodVersion(knownGoodValue, newValue);
|
|
319
|
+
if (Object.is(knownGoodValue, validated)) {
|
|
320
|
+
return knownGoodValue;
|
|
321
|
+
}
|
|
322
|
+
return otherValidationFn(validated);
|
|
323
|
+
},
|
|
324
|
+
true
|
|
325
|
+
// skipSameValueCheck: refine is designed to transform values
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
check(nameOrCheckFn, checkFn) {
|
|
329
|
+
if (typeof nameOrCheckFn === "string") {
|
|
330
|
+
return this.refine((value) => {
|
|
331
|
+
prefixError(`(check ${nameOrCheckFn})`, () => checkFn(value));
|
|
332
|
+
return value;
|
|
333
|
+
});
|
|
334
|
+
} else {
|
|
335
|
+
return this.refine((value) => {
|
|
336
|
+
nameOrCheckFn(value);
|
|
337
|
+
return value;
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
var ArrayOfValidator = class extends Validator {
|
|
343
|
+
/**
|
|
344
|
+
* Creates a new ArrayOfValidator.
|
|
345
|
+
*
|
|
346
|
+
* itemValidator - Validator used to validate each array element
|
|
347
|
+
*/
|
|
348
|
+
constructor(itemValidator) {
|
|
349
|
+
super(
|
|
350
|
+
(value) => {
|
|
351
|
+
const arr = array.validate(value);
|
|
352
|
+
for (let i = 0; i < arr.length; i++) {
|
|
353
|
+
{
|
|
354
|
+
prefixError(i, () => itemValidator.validate(arr[i]));
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
return arr;
|
|
358
|
+
},
|
|
359
|
+
(knownGoodValue, newValue) => {
|
|
360
|
+
if (Object.is(knownGoodValue, newValue)) {
|
|
361
|
+
return knownGoodValue;
|
|
362
|
+
}
|
|
363
|
+
if (!itemValidator.validateUsingKnownGoodVersion) return this.validate(newValue);
|
|
364
|
+
const arr = array.validate(newValue);
|
|
365
|
+
let isDifferent = knownGoodValue.length !== arr.length;
|
|
366
|
+
for (let i = 0; i < arr.length; i++) {
|
|
367
|
+
const item = arr[i];
|
|
368
|
+
if (i >= knownGoodValue.length) {
|
|
369
|
+
isDifferent = true;
|
|
370
|
+
{
|
|
371
|
+
prefixError(i, () => itemValidator.validate(item));
|
|
372
|
+
}
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
if (Object.is(knownGoodValue[i], item)) {
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
{
|
|
379
|
+
const checkedItem = prefixError(
|
|
380
|
+
i,
|
|
381
|
+
() => itemValidator.validateUsingKnownGoodVersion(knownGoodValue[i], item)
|
|
382
|
+
);
|
|
383
|
+
if (!Object.is(checkedItem, knownGoodValue[i])) {
|
|
384
|
+
isDifferent = true;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
return isDifferent ? newValue : knownGoodValue;
|
|
389
|
+
}
|
|
390
|
+
);
|
|
391
|
+
this.itemValidator = itemValidator;
|
|
392
|
+
}
|
|
393
|
+
itemValidator;
|
|
394
|
+
/**
|
|
395
|
+
* Returns a new validator that ensures the array is not empty.
|
|
396
|
+
*
|
|
397
|
+
* @returns A new validator that rejects empty arrays
|
|
398
|
+
* @throws ValidationError When the array is empty
|
|
399
|
+
* @example
|
|
400
|
+
* ```ts
|
|
401
|
+
* const nonEmptyStrings = T.arrayOf(T.string).nonEmpty()
|
|
402
|
+
* nonEmptyStrings.validate(["hello"]) // Valid
|
|
403
|
+
* nonEmptyStrings.validate([]) // Throws ValidationError
|
|
404
|
+
* ```
|
|
405
|
+
*/
|
|
406
|
+
nonEmpty() {
|
|
407
|
+
return this.check((value) => {
|
|
408
|
+
if (value.length === 0) {
|
|
409
|
+
throw new ValidationError("Expected a non-empty array");
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Returns a new validator that ensures the array has more than one element.
|
|
415
|
+
*
|
|
416
|
+
* @returns A new validator that requires at least 2 elements
|
|
417
|
+
* @throws ValidationError When the array has 1 or fewer elements
|
|
418
|
+
* @example
|
|
419
|
+
* ```ts
|
|
420
|
+
* const multipleItems = T.arrayOf(T.string).lengthGreaterThan1()
|
|
421
|
+
* multipleItems.validate(["a", "b"]) // Valid
|
|
422
|
+
* multipleItems.validate(["a"]) // Throws ValidationError
|
|
423
|
+
* ```
|
|
424
|
+
*/
|
|
425
|
+
lengthGreaterThan1() {
|
|
426
|
+
return this.check((value) => {
|
|
427
|
+
if (value.length <= 1) {
|
|
428
|
+
throw new ValidationError("Expected an array with length greater than 1");
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
var ObjectValidator = class _ObjectValidator extends Validator {
|
|
434
|
+
/**
|
|
435
|
+
* Creates a new ObjectValidator.
|
|
436
|
+
*
|
|
437
|
+
* config - Object mapping property names to their validators
|
|
438
|
+
* shouldAllowUnknownProperties - Whether to allow properties not defined in config
|
|
439
|
+
*/
|
|
440
|
+
constructor(config, shouldAllowUnknownProperties = false) {
|
|
441
|
+
super(
|
|
442
|
+
(object2) => {
|
|
443
|
+
if (typeof object2 !== "object" || object2 === null) {
|
|
444
|
+
throw new ValidationError(`Expected object, got ${typeToString(object2)}`);
|
|
445
|
+
}
|
|
446
|
+
for (const key in config) {
|
|
447
|
+
if (!hasOwnProperty(config, key)) continue;
|
|
448
|
+
const validator = config[key];
|
|
449
|
+
{
|
|
450
|
+
prefixError(key, () => {
|
|
451
|
+
;
|
|
452
|
+
validator.validate(getOwnProperty(object2, key));
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
if (!shouldAllowUnknownProperties) {
|
|
457
|
+
for (const key of Object.keys(object2)) {
|
|
458
|
+
if (!hasOwnProperty(config, key)) {
|
|
459
|
+
throw new ValidationError(`Unexpected property`, [key]);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
return object2;
|
|
464
|
+
},
|
|
465
|
+
(knownGoodValue, newValue) => {
|
|
466
|
+
if (Object.is(knownGoodValue, newValue)) {
|
|
467
|
+
return knownGoodValue;
|
|
468
|
+
}
|
|
469
|
+
if (typeof newValue !== "object" || newValue === null) {
|
|
470
|
+
throw new ValidationError(`Expected object, got ${typeToString(newValue)}`);
|
|
471
|
+
}
|
|
472
|
+
let isDifferent = false;
|
|
473
|
+
for (const key in config) {
|
|
474
|
+
if (!hasOwnProperty(config, key)) continue;
|
|
475
|
+
const validator = config[key];
|
|
476
|
+
const prev = getOwnProperty(knownGoodValue, key);
|
|
477
|
+
const next = getOwnProperty(newValue, key);
|
|
478
|
+
if (Object.is(prev, next)) {
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
{
|
|
482
|
+
const checked = prefixError(key, () => {
|
|
483
|
+
const validatable = validator;
|
|
484
|
+
if (validatable.validateUsingKnownGoodVersion) {
|
|
485
|
+
return validatable.validateUsingKnownGoodVersion(prev, next);
|
|
486
|
+
} else {
|
|
487
|
+
return validatable.validate(next);
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
if (!Object.is(checked, prev)) {
|
|
491
|
+
isDifferent = true;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
if (!shouldAllowUnknownProperties) {
|
|
496
|
+
for (const key of Object.keys(newValue)) {
|
|
497
|
+
if (!hasOwnProperty(config, key)) {
|
|
498
|
+
throw new ValidationError(`Unexpected property`, [key]);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
for (const key of Object.keys(knownGoodValue)) {
|
|
503
|
+
if (!hasOwnProperty(newValue, key)) {
|
|
504
|
+
isDifferent = true;
|
|
505
|
+
break;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
return isDifferent ? newValue : knownGoodValue;
|
|
509
|
+
}
|
|
510
|
+
);
|
|
511
|
+
this.config = config;
|
|
512
|
+
this.shouldAllowUnknownProperties = shouldAllowUnknownProperties;
|
|
513
|
+
}
|
|
514
|
+
config;
|
|
515
|
+
shouldAllowUnknownProperties;
|
|
516
|
+
/**
|
|
517
|
+
* Returns a new validator that allows unknown properties in the validated object.
|
|
518
|
+
*
|
|
519
|
+
* @returns A new ObjectValidator that accepts extra properties
|
|
520
|
+
* @example
|
|
521
|
+
* ```ts
|
|
522
|
+
* const flexibleUser = T.object({ name: T.string }).allowUnknownProperties()
|
|
523
|
+
* flexibleUser.validate({ name: "Alice", extra: "allowed" }) // Valid
|
|
524
|
+
* ```
|
|
525
|
+
*/
|
|
526
|
+
allowUnknownProperties() {
|
|
527
|
+
return new _ObjectValidator(this.config, true);
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Creates a new ObjectValidator by extending this validator with additional properties.
|
|
531
|
+
*
|
|
532
|
+
* @param extension - Object mapping new property names to their validators
|
|
533
|
+
* @returns A new ObjectValidator that validates both original and extended properties
|
|
534
|
+
* @example
|
|
535
|
+
* ```ts
|
|
536
|
+
* const baseUser = T.object({ name: T.string, age: T.number })
|
|
537
|
+
* const adminUser = baseUser.extend({
|
|
538
|
+
* permissions: T.arrayOf(T.string),
|
|
539
|
+
* isAdmin: T.boolean
|
|
540
|
+
* })
|
|
541
|
+
* // adminUser validates: { name: string; age: number; permissions: string[]; isAdmin: boolean }
|
|
542
|
+
* ```
|
|
543
|
+
*/
|
|
544
|
+
extend(extension) {
|
|
545
|
+
return new _ObjectValidator({ ...this.config, ...extension });
|
|
546
|
+
}
|
|
547
|
+
};
|
|
548
|
+
var UnionValidator = class _UnionValidator extends Validator {
|
|
549
|
+
/**
|
|
550
|
+
* Creates a new UnionValidator.
|
|
551
|
+
*
|
|
552
|
+
* key - The discriminator property name used to determine the variant
|
|
553
|
+
* config - Object mapping variant names to their validators
|
|
554
|
+
* unknownValueValidation - Function to handle unknown variants
|
|
555
|
+
* useNumberKeys - Whether the discriminator uses number keys instead of strings
|
|
556
|
+
*/
|
|
557
|
+
constructor(key, config, unknownValueValidation, useNumberKeys) {
|
|
558
|
+
super(
|
|
559
|
+
(input) => {
|
|
560
|
+
this.expectObject(input);
|
|
561
|
+
const { matchingSchema, variant } = this.getMatchingSchemaAndVariant(input);
|
|
562
|
+
if (matchingSchema === void 0) {
|
|
563
|
+
return this.unknownValueValidation(input, variant);
|
|
564
|
+
}
|
|
565
|
+
return prefixError(`(${key} = ${variant})`, () => matchingSchema.validate(input));
|
|
566
|
+
},
|
|
567
|
+
(prevValue, newValue) => {
|
|
568
|
+
this.expectObject(newValue);
|
|
569
|
+
this.expectObject(prevValue);
|
|
570
|
+
const { matchingSchema, variant } = this.getMatchingSchemaAndVariant(newValue);
|
|
571
|
+
if (matchingSchema === void 0) {
|
|
572
|
+
return this.unknownValueValidation(newValue, variant);
|
|
573
|
+
}
|
|
574
|
+
if (getOwnProperty(prevValue, key) !== getOwnProperty(newValue, key)) {
|
|
575
|
+
return prefixError(`(${key} = ${variant})`, () => matchingSchema.validate(newValue));
|
|
576
|
+
}
|
|
577
|
+
return prefixError(`(${key} = ${variant})`, () => {
|
|
578
|
+
if (matchingSchema.validateUsingKnownGoodVersion) {
|
|
579
|
+
return matchingSchema.validateUsingKnownGoodVersion(prevValue, newValue);
|
|
580
|
+
} else {
|
|
581
|
+
return matchingSchema.validate(newValue);
|
|
582
|
+
}
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
);
|
|
586
|
+
this.key = key;
|
|
587
|
+
this.config = config;
|
|
588
|
+
this.unknownValueValidation = unknownValueValidation;
|
|
589
|
+
this.useNumberKeys = useNumberKeys;
|
|
590
|
+
}
|
|
591
|
+
key;
|
|
592
|
+
config;
|
|
593
|
+
unknownValueValidation;
|
|
594
|
+
useNumberKeys;
|
|
595
|
+
expectObject(value) {
|
|
596
|
+
if (typeof value !== "object" || value === null) {
|
|
597
|
+
throw new ValidationError(`Expected an object, got ${typeToString(value)}`, []);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
getMatchingSchemaAndVariant(object2) {
|
|
601
|
+
const variant = getOwnProperty(object2, this.key);
|
|
602
|
+
if (!this.useNumberKeys && typeof variant !== "string") {
|
|
603
|
+
throw new ValidationError(
|
|
604
|
+
`Expected a string for key "${this.key}", got ${typeToString(variant)}`
|
|
605
|
+
);
|
|
606
|
+
} else if (this.useNumberKeys) {
|
|
607
|
+
const numVariant = Number(variant);
|
|
608
|
+
if (numVariant - numVariant !== 0) {
|
|
609
|
+
throw new ValidationError(
|
|
610
|
+
`Expected a number for key "${this.key}", got "${variant}"`
|
|
611
|
+
);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
const matchingSchema = hasOwnProperty(this.config, variant) ? this.config[variant] : void 0;
|
|
615
|
+
return { matchingSchema, variant };
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* Returns a new UnionValidator that can handle unknown variants using the provided function.
|
|
619
|
+
*
|
|
620
|
+
* @param unknownValueValidation - Function to validate/transform unknown variants
|
|
621
|
+
* @returns A new UnionValidator that accepts unknown variants
|
|
622
|
+
* @example
|
|
623
|
+
* ```ts
|
|
624
|
+
* const shapeValidator = T.union('type', { circle: circleValidator })
|
|
625
|
+
* .validateUnknownVariants((obj, variant) => {
|
|
626
|
+
* console.warn(`Unknown shape type: ${variant}`)
|
|
627
|
+
* return obj as UnknownShape
|
|
628
|
+
* })
|
|
629
|
+
* ```
|
|
630
|
+
*/
|
|
631
|
+
validateUnknownVariants(unknownValueValidation) {
|
|
632
|
+
return new _UnionValidator(this.key, this.config, unknownValueValidation, this.useNumberKeys);
|
|
633
|
+
}
|
|
634
|
+
};
|
|
635
|
+
var DictValidator = class extends Validator {
|
|
636
|
+
/**
|
|
637
|
+
* Creates a new DictValidator.
|
|
638
|
+
*
|
|
639
|
+
* keyValidator - Validator for object keys
|
|
640
|
+
* valueValidator - Validator for object values
|
|
641
|
+
*/
|
|
642
|
+
constructor(keyValidator, valueValidator) {
|
|
643
|
+
super(
|
|
644
|
+
(object2) => {
|
|
645
|
+
if (typeof object2 !== "object" || object2 === null) {
|
|
646
|
+
throw new ValidationError(`Expected object, got ${typeToString(object2)}`);
|
|
647
|
+
}
|
|
648
|
+
for (const key in object2) {
|
|
649
|
+
if (!hasOwnProperty(object2, key)) continue;
|
|
650
|
+
{
|
|
651
|
+
prefixError(key, () => {
|
|
652
|
+
keyValidator.validate(key);
|
|
653
|
+
valueValidator.validate(object2[key]);
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
return object2;
|
|
658
|
+
},
|
|
659
|
+
(knownGoodValue, newValue) => {
|
|
660
|
+
if (typeof newValue !== "object" || newValue === null) {
|
|
661
|
+
throw new ValidationError(`Expected object, got ${typeToString(newValue)}`);
|
|
662
|
+
}
|
|
663
|
+
const newObj = newValue;
|
|
664
|
+
let isDifferent = false;
|
|
665
|
+
let newKeyCount = 0;
|
|
666
|
+
for (const key in newObj) {
|
|
667
|
+
if (!hasOwnProperty(newObj, key)) continue;
|
|
668
|
+
newKeyCount++;
|
|
669
|
+
const next = newObj[key];
|
|
670
|
+
if (!hasOwnProperty(knownGoodValue, key)) {
|
|
671
|
+
isDifferent = true;
|
|
672
|
+
{
|
|
673
|
+
prefixError(key, () => {
|
|
674
|
+
keyValidator.validate(key);
|
|
675
|
+
valueValidator.validate(next);
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
continue;
|
|
679
|
+
}
|
|
680
|
+
const prev = knownGoodValue[key];
|
|
681
|
+
if (Object.is(prev, next)) {
|
|
682
|
+
continue;
|
|
683
|
+
}
|
|
684
|
+
{
|
|
685
|
+
const checked = prefixError(key, () => {
|
|
686
|
+
if (valueValidator.validateUsingKnownGoodVersion) {
|
|
687
|
+
return valueValidator.validateUsingKnownGoodVersion(prev, next);
|
|
688
|
+
} else {
|
|
689
|
+
return valueValidator.validate(next);
|
|
690
|
+
}
|
|
691
|
+
});
|
|
692
|
+
if (!Object.is(checked, prev)) {
|
|
693
|
+
isDifferent = true;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
if (!isDifferent) {
|
|
698
|
+
let oldKeyCount = 0;
|
|
699
|
+
for (const key in knownGoodValue) {
|
|
700
|
+
if (hasOwnProperty(knownGoodValue, key)) {
|
|
701
|
+
oldKeyCount++;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
if (oldKeyCount !== newKeyCount) {
|
|
705
|
+
isDifferent = true;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
return isDifferent ? newValue : knownGoodValue;
|
|
709
|
+
}
|
|
710
|
+
);
|
|
711
|
+
this.keyValidator = keyValidator;
|
|
712
|
+
this.valueValidator = valueValidator;
|
|
713
|
+
}
|
|
714
|
+
keyValidator;
|
|
715
|
+
valueValidator;
|
|
716
|
+
};
|
|
717
|
+
function typeofValidator(type) {
|
|
718
|
+
return new Validator((value) => {
|
|
719
|
+
if (typeof value !== type) {
|
|
720
|
+
throw new ValidationError(`Expected ${type}, got ${typeToString(value)}`);
|
|
721
|
+
}
|
|
722
|
+
return value;
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
var unknown = new Validator((value) => value);
|
|
726
|
+
var any = new Validator((value) => value);
|
|
727
|
+
var string = typeofValidator("string");
|
|
728
|
+
var number = new Validator((value) => {
|
|
729
|
+
if (Number.isFinite(value)) {
|
|
730
|
+
return value;
|
|
731
|
+
}
|
|
732
|
+
if (typeof value !== "number") {
|
|
733
|
+
throw new ValidationError(`Expected number, got ${typeToString(value)}`);
|
|
734
|
+
}
|
|
735
|
+
if (value !== value) {
|
|
736
|
+
throw new ValidationError("Expected a number, got NaN");
|
|
737
|
+
}
|
|
738
|
+
throw new ValidationError(`Expected a finite number, got ${value}`);
|
|
739
|
+
});
|
|
740
|
+
var positiveNumber = new Validator((value) => {
|
|
741
|
+
if (Number.isFinite(value) && value >= 0) {
|
|
742
|
+
return value;
|
|
743
|
+
}
|
|
744
|
+
if (typeof value !== "number") {
|
|
745
|
+
throw new ValidationError(`Expected number, got ${typeToString(value)}`);
|
|
746
|
+
}
|
|
747
|
+
if (value !== value) {
|
|
748
|
+
throw new ValidationError("Expected a number, got NaN");
|
|
749
|
+
}
|
|
750
|
+
if (value < 0) {
|
|
751
|
+
throw new ValidationError(`Expected a positive number, got ${value}`);
|
|
752
|
+
}
|
|
753
|
+
throw new ValidationError(`Expected a finite number, got ${value}`);
|
|
754
|
+
});
|
|
755
|
+
var nonZeroNumber = new Validator((value) => {
|
|
756
|
+
if (Number.isFinite(value) && value > 0) {
|
|
757
|
+
return value;
|
|
758
|
+
}
|
|
759
|
+
if (typeof value !== "number") {
|
|
760
|
+
throw new ValidationError(`Expected number, got ${typeToString(value)}`);
|
|
761
|
+
}
|
|
762
|
+
if (value !== value) {
|
|
763
|
+
throw new ValidationError("Expected a number, got NaN");
|
|
764
|
+
}
|
|
765
|
+
if (value <= 0) {
|
|
766
|
+
throw new ValidationError(`Expected a non-zero positive number, got ${value}`);
|
|
767
|
+
}
|
|
768
|
+
throw new ValidationError(`Expected a finite number, got ${value}`);
|
|
769
|
+
});
|
|
770
|
+
var nonZeroFiniteNumber = new Validator((value) => {
|
|
771
|
+
if (Number.isFinite(value) && value !== 0) {
|
|
772
|
+
return value;
|
|
773
|
+
}
|
|
774
|
+
if (typeof value !== "number") {
|
|
775
|
+
throw new ValidationError(`Expected number, got ${typeToString(value)}`);
|
|
776
|
+
}
|
|
777
|
+
if (value !== value) {
|
|
778
|
+
throw new ValidationError("Expected a number, got NaN");
|
|
779
|
+
}
|
|
780
|
+
if (value === 0) {
|
|
781
|
+
throw new ValidationError(`Expected a non-zero number, got 0`);
|
|
782
|
+
}
|
|
783
|
+
throw new ValidationError(`Expected a finite number, got ${value}`);
|
|
784
|
+
});
|
|
785
|
+
var unitInterval = new Validator((value) => {
|
|
786
|
+
if (Number.isFinite(value) && value >= 0 && value <= 1) {
|
|
787
|
+
return value;
|
|
788
|
+
}
|
|
789
|
+
if (typeof value !== "number") {
|
|
790
|
+
throw new ValidationError(`Expected number, got ${typeToString(value)}`);
|
|
791
|
+
}
|
|
792
|
+
if (value !== value) {
|
|
793
|
+
throw new ValidationError("Expected a number, got NaN");
|
|
794
|
+
}
|
|
795
|
+
throw new ValidationError(`Expected a number between 0 and 1, got ${value}`);
|
|
796
|
+
});
|
|
797
|
+
var integer = new Validator((value) => {
|
|
798
|
+
if (Number.isInteger(value)) {
|
|
799
|
+
return value;
|
|
800
|
+
}
|
|
801
|
+
if (typeof value !== "number") {
|
|
802
|
+
throw new ValidationError(`Expected number, got ${typeToString(value)}`);
|
|
803
|
+
}
|
|
804
|
+
if (value !== value) {
|
|
805
|
+
throw new ValidationError("Expected a number, got NaN");
|
|
806
|
+
}
|
|
807
|
+
if (value - value !== 0) {
|
|
808
|
+
throw new ValidationError(`Expected a finite number, got ${value}`);
|
|
809
|
+
}
|
|
810
|
+
throw new ValidationError(`Expected an integer, got ${value}`);
|
|
811
|
+
});
|
|
812
|
+
var positiveInteger = new Validator((value) => {
|
|
813
|
+
if (Number.isInteger(value) && value >= 0) {
|
|
814
|
+
return value;
|
|
815
|
+
}
|
|
816
|
+
if (typeof value !== "number") {
|
|
817
|
+
throw new ValidationError(`Expected number, got ${typeToString(value)}`);
|
|
818
|
+
}
|
|
819
|
+
if (value !== value) {
|
|
820
|
+
throw new ValidationError("Expected a number, got NaN");
|
|
821
|
+
}
|
|
822
|
+
if (value - value !== 0) {
|
|
823
|
+
throw new ValidationError(`Expected a finite number, got ${value}`);
|
|
824
|
+
}
|
|
825
|
+
if (value < 0) {
|
|
826
|
+
throw new ValidationError(`Expected a positive integer, got ${value}`);
|
|
827
|
+
}
|
|
828
|
+
throw new ValidationError(`Expected an integer, got ${value}`);
|
|
829
|
+
});
|
|
830
|
+
var nonZeroInteger = new Validator((value) => {
|
|
831
|
+
if (Number.isInteger(value) && value > 0) {
|
|
832
|
+
return value;
|
|
833
|
+
}
|
|
834
|
+
if (typeof value !== "number") {
|
|
835
|
+
throw new ValidationError(`Expected number, got ${typeToString(value)}`);
|
|
836
|
+
}
|
|
837
|
+
if (value !== value) {
|
|
838
|
+
throw new ValidationError("Expected a number, got NaN");
|
|
839
|
+
}
|
|
840
|
+
if (value - value !== 0) {
|
|
841
|
+
throw new ValidationError(`Expected a finite number, got ${value}`);
|
|
842
|
+
}
|
|
843
|
+
if (value <= 0) {
|
|
844
|
+
throw new ValidationError(`Expected a non-zero positive integer, got ${value}`);
|
|
845
|
+
}
|
|
846
|
+
throw new ValidationError(`Expected an integer, got ${value}`);
|
|
847
|
+
});
|
|
848
|
+
var boolean = typeofValidator("boolean");
|
|
849
|
+
var bigint = typeofValidator("bigint");
|
|
850
|
+
function literal(expectedValue) {
|
|
851
|
+
return new Validator((actualValue) => {
|
|
852
|
+
if (actualValue !== expectedValue) {
|
|
853
|
+
throw new ValidationError(`Expected ${expectedValue}, got ${JSON.stringify(actualValue)}`);
|
|
854
|
+
}
|
|
855
|
+
return expectedValue;
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
var array = new Validator((value) => {
|
|
859
|
+
if (!Array.isArray(value)) {
|
|
860
|
+
throw new ValidationError(`Expected an array, got ${typeToString(value)}`);
|
|
861
|
+
}
|
|
862
|
+
return value;
|
|
863
|
+
});
|
|
864
|
+
function arrayOf(itemValidator) {
|
|
865
|
+
return new ArrayOfValidator(itemValidator);
|
|
866
|
+
}
|
|
867
|
+
var unknownObject = new Validator((value) => {
|
|
868
|
+
if (typeof value !== "object" || value === null) {
|
|
869
|
+
throw new ValidationError(`Expected object, got ${typeToString(value)}`);
|
|
870
|
+
}
|
|
871
|
+
return value;
|
|
872
|
+
});
|
|
873
|
+
function object(config) {
|
|
874
|
+
return new ObjectValidator(config);
|
|
875
|
+
}
|
|
876
|
+
function isPlainObject(value) {
|
|
877
|
+
return typeof value === "object" && value !== null && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null || Object.getPrototypeOf(value) === STRUCTURED_CLONE_OBJECT_PROTOTYPE);
|
|
878
|
+
}
|
|
879
|
+
function isValidJson(value) {
|
|
880
|
+
if (value === null || typeof value === "number" || typeof value === "string" || typeof value === "boolean") {
|
|
881
|
+
return true;
|
|
882
|
+
}
|
|
883
|
+
if (Array.isArray(value)) {
|
|
884
|
+
return value.every(isValidJson);
|
|
885
|
+
}
|
|
886
|
+
if (isPlainObject(value)) {
|
|
887
|
+
return Object.values(value).every(isValidJson);
|
|
888
|
+
}
|
|
889
|
+
return false;
|
|
890
|
+
}
|
|
891
|
+
var jsonValue = new Validator(
|
|
892
|
+
(value) => {
|
|
893
|
+
if (isValidJson(value)) {
|
|
894
|
+
return value;
|
|
895
|
+
}
|
|
896
|
+
throw new ValidationError(`Expected json serializable value, got ${typeof value}`);
|
|
897
|
+
},
|
|
898
|
+
(knownGoodValue, newValue) => {
|
|
899
|
+
if (Array.isArray(knownGoodValue) && Array.isArray(newValue)) {
|
|
900
|
+
let isDifferent = knownGoodValue.length !== newValue.length;
|
|
901
|
+
for (let i = 0; i < newValue.length; i++) {
|
|
902
|
+
if (i >= knownGoodValue.length) {
|
|
903
|
+
isDifferent = true;
|
|
904
|
+
jsonValue.validate(newValue[i]);
|
|
905
|
+
continue;
|
|
906
|
+
}
|
|
907
|
+
const prev = knownGoodValue[i];
|
|
908
|
+
const next = newValue[i];
|
|
909
|
+
if (Object.is(prev, next)) {
|
|
910
|
+
continue;
|
|
911
|
+
}
|
|
912
|
+
const checked = jsonValue.validateUsingKnownGoodVersion(prev, next);
|
|
913
|
+
if (!Object.is(checked, prev)) {
|
|
914
|
+
isDifferent = true;
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
return isDifferent ? newValue : knownGoodValue;
|
|
918
|
+
} else if (isPlainObject(knownGoodValue) && isPlainObject(newValue)) {
|
|
919
|
+
let isDifferent = false;
|
|
920
|
+
for (const key of Object.keys(newValue)) {
|
|
921
|
+
if (!hasOwnProperty(knownGoodValue, key)) {
|
|
922
|
+
isDifferent = true;
|
|
923
|
+
jsonValue.validate(newValue[key]);
|
|
924
|
+
continue;
|
|
925
|
+
}
|
|
926
|
+
const prev = knownGoodValue[key];
|
|
927
|
+
const next = newValue[key];
|
|
928
|
+
if (Object.is(prev, next)) {
|
|
929
|
+
continue;
|
|
930
|
+
}
|
|
931
|
+
const checked = jsonValue.validateUsingKnownGoodVersion(prev, next);
|
|
932
|
+
if (!Object.is(checked, prev)) {
|
|
933
|
+
isDifferent = true;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
for (const key of Object.keys(knownGoodValue)) {
|
|
937
|
+
if (!hasOwnProperty(newValue, key)) {
|
|
938
|
+
isDifferent = true;
|
|
939
|
+
break;
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
return isDifferent ? newValue : knownGoodValue;
|
|
943
|
+
} else {
|
|
944
|
+
return jsonValue.validate(newValue);
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
);
|
|
948
|
+
function jsonDict() {
|
|
949
|
+
return dict(string, jsonValue);
|
|
950
|
+
}
|
|
951
|
+
function dict(keyValidator, valueValidator) {
|
|
952
|
+
return new DictValidator(keyValidator, valueValidator);
|
|
953
|
+
}
|
|
954
|
+
function union(key, config) {
|
|
955
|
+
return new UnionValidator(
|
|
956
|
+
key,
|
|
957
|
+
config,
|
|
958
|
+
(_unknownValue, unknownVariant) => {
|
|
959
|
+
throw new ValidationError(
|
|
960
|
+
`Expected one of ${Object.keys(config).map((key2) => JSON.stringify(key2)).join(" or ")}, got ${JSON.stringify(unknownVariant)}`,
|
|
961
|
+
[key]
|
|
962
|
+
);
|
|
963
|
+
},
|
|
964
|
+
false
|
|
965
|
+
);
|
|
966
|
+
}
|
|
967
|
+
function numberUnion(key, config) {
|
|
968
|
+
return new UnionValidator(
|
|
969
|
+
key,
|
|
970
|
+
config,
|
|
971
|
+
(unknownValue, unknownVariant) => {
|
|
972
|
+
throw new ValidationError(
|
|
973
|
+
`Expected one of ${Object.keys(config).map((key2) => JSON.stringify(key2)).join(" or ")}, got ${JSON.stringify(unknownVariant)}`,
|
|
974
|
+
[key]
|
|
975
|
+
);
|
|
976
|
+
},
|
|
977
|
+
true
|
|
978
|
+
);
|
|
979
|
+
}
|
|
980
|
+
function model(name, validator) {
|
|
981
|
+
return new Validator(
|
|
982
|
+
(value) => {
|
|
983
|
+
return prefixError(name, () => validator.validate(value));
|
|
984
|
+
},
|
|
985
|
+
(prevValue, newValue) => {
|
|
986
|
+
return prefixError(name, () => {
|
|
987
|
+
if (validator.validateUsingKnownGoodVersion) {
|
|
988
|
+
return validator.validateUsingKnownGoodVersion(prevValue, newValue);
|
|
989
|
+
} else {
|
|
990
|
+
return validator.validate(newValue);
|
|
991
|
+
}
|
|
992
|
+
});
|
|
993
|
+
}
|
|
994
|
+
);
|
|
995
|
+
}
|
|
996
|
+
function setEnum(values) {
|
|
997
|
+
return new Validator((value) => {
|
|
998
|
+
if (!values.has(value)) {
|
|
999
|
+
const valuesString = Array.from(values, (value2) => JSON.stringify(value2)).join(" or ");
|
|
1000
|
+
throw new ValidationError(`Expected ${valuesString}, got ${value}`);
|
|
1001
|
+
}
|
|
1002
|
+
return value;
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
function optional(validator) {
|
|
1006
|
+
return new Validator(
|
|
1007
|
+
(value) => {
|
|
1008
|
+
if (value === void 0) return void 0;
|
|
1009
|
+
return validator.validate(value);
|
|
1010
|
+
},
|
|
1011
|
+
(knownGoodValue, newValue) => {
|
|
1012
|
+
if (newValue === void 0) return void 0;
|
|
1013
|
+
if (validator.validateUsingKnownGoodVersion && knownGoodValue !== void 0) {
|
|
1014
|
+
return validator.validateUsingKnownGoodVersion(knownGoodValue, newValue);
|
|
1015
|
+
}
|
|
1016
|
+
return validator.validate(newValue);
|
|
1017
|
+
},
|
|
1018
|
+
// Propagate skipSameValueCheck from inner validator to allow refine wrappers
|
|
1019
|
+
validator instanceof Validator && validator.skipSameValueCheck
|
|
1020
|
+
);
|
|
1021
|
+
}
|
|
1022
|
+
function nullable(validator) {
|
|
1023
|
+
return new Validator(
|
|
1024
|
+
(value) => {
|
|
1025
|
+
if (value === null) return null;
|
|
1026
|
+
return validator.validate(value);
|
|
1027
|
+
},
|
|
1028
|
+
(knownGoodValue, newValue) => {
|
|
1029
|
+
if (newValue === null) return null;
|
|
1030
|
+
if (validator.validateUsingKnownGoodVersion && knownGoodValue !== null) {
|
|
1031
|
+
return validator.validateUsingKnownGoodVersion(knownGoodValue, newValue);
|
|
1032
|
+
}
|
|
1033
|
+
return validator.validate(newValue);
|
|
1034
|
+
},
|
|
1035
|
+
// Propagate skipSameValueCheck from inner validator to allow refine wrappers
|
|
1036
|
+
validator instanceof Validator && validator.skipSameValueCheck
|
|
1037
|
+
);
|
|
1038
|
+
}
|
|
1039
|
+
function literalEnum(...values) {
|
|
1040
|
+
return setEnum(new Set(values));
|
|
1041
|
+
}
|
|
1042
|
+
function parseUrl(str) {
|
|
1043
|
+
try {
|
|
1044
|
+
return new URL(str);
|
|
1045
|
+
} catch {
|
|
1046
|
+
if (str.startsWith("/") || str.startsWith("./")) {
|
|
1047
|
+
try {
|
|
1048
|
+
return new URL(str, "http://example.com");
|
|
1049
|
+
} catch {
|
|
1050
|
+
throw new ValidationError(`Expected a valid url, got ${JSON.stringify(str)}`);
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
throw new ValidationError(`Expected a valid url, got ${JSON.stringify(str)}`);
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
var validLinkProtocols = /* @__PURE__ */ new Set(["http:", "https:", "mailto:"]);
|
|
1057
|
+
var linkUrl = string.check((value) => {
|
|
1058
|
+
if (value === "") return;
|
|
1059
|
+
const url = parseUrl(value);
|
|
1060
|
+
if (!validLinkProtocols.has(url.protocol.toLowerCase())) {
|
|
1061
|
+
throw new ValidationError(
|
|
1062
|
+
`Expected a valid url, got ${JSON.stringify(value)} (invalid protocol)`
|
|
1063
|
+
);
|
|
1064
|
+
}
|
|
1065
|
+
});
|
|
1066
|
+
var validSrcProtocols = /* @__PURE__ */ new Set(["http:", "https:", "data:", "asset:"]);
|
|
1067
|
+
var srcUrl = string.check((value) => {
|
|
1068
|
+
if (value === "") return;
|
|
1069
|
+
const url = parseUrl(value);
|
|
1070
|
+
if (!validSrcProtocols.has(url.protocol.toLowerCase())) {
|
|
1071
|
+
throw new ValidationError(
|
|
1072
|
+
`Expected a valid url, got ${JSON.stringify(value)} (invalid protocol)`
|
|
1073
|
+
);
|
|
1074
|
+
}
|
|
1075
|
+
});
|
|
1076
|
+
var httpUrl = string.check((value) => {
|
|
1077
|
+
if (value === "") return;
|
|
1078
|
+
const url = parseUrl(value);
|
|
1079
|
+
if (!url.protocol.toLowerCase().match(/^https?:$/)) {
|
|
1080
|
+
throw new ValidationError(
|
|
1081
|
+
`Expected a valid url, got ${JSON.stringify(value)} (invalid protocol)`
|
|
1082
|
+
);
|
|
1083
|
+
}
|
|
1084
|
+
});
|
|
1085
|
+
var indexKey = string.refine((key) => {
|
|
1086
|
+
try {
|
|
1087
|
+
validateIndexKey(key);
|
|
1088
|
+
return key;
|
|
1089
|
+
} catch {
|
|
1090
|
+
throw new ValidationError(`Expected an index key, got ${JSON.stringify(key)}`);
|
|
1091
|
+
}
|
|
1092
|
+
});
|
|
1093
|
+
function or(v1, v2) {
|
|
1094
|
+
return new Validator((value) => {
|
|
1095
|
+
try {
|
|
1096
|
+
return v1.validate(value);
|
|
1097
|
+
} catch {
|
|
1098
|
+
return v2.validate(value);
|
|
1099
|
+
}
|
|
1100
|
+
});
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
// src/index.ts
|
|
1104
|
+
registerDrawLibraryVersion(
|
|
1105
|
+
"@ibodr/validate",
|
|
1106
|
+
"0.0.0",
|
|
1107
|
+
"esm"
|
|
1108
|
+
);
|
|
1109
|
+
|
|
1110
|
+
export { ArrayOfValidator, DictValidator, ObjectValidator, validation_exports as T, UnionValidator, Validator };
|
|
1111
|
+
//# sourceMappingURL=index.mjs.map
|
|
1112
|
+
//# sourceMappingURL=index.mjs.map
|