@tldraw/validate 4.0.2 → 4.1.0-canary.0259516ffb8c
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-cjs/index.d.ts +771 -77
- package/dist-cjs/index.js +2 -2
- package/dist-cjs/lib/validation.js +226 -15
- package/dist-cjs/lib/validation.js.map +2 -2
- package/dist-esm/index.d.mts +771 -77
- package/dist-esm/index.mjs +1 -1
- package/dist-esm/lib/validation.mjs +226 -15
- package/dist-esm/lib/validation.mjs.map +2 -2
- package/package.json +2 -2
- package/src/lib/validation.ts +778 -78
- package/src/test/validation.test.ts +54 -3
package/dist-cjs/index.d.ts
CHANGED
|
@@ -3,124 +3,319 @@ import { JsonValue } from '@tldraw/utils';
|
|
|
3
3
|
import { MakeUndefinedOptional } from '@tldraw/utils';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* Validator that accepts any value and types it as 'any'. This should generally be avoided
|
|
7
|
+
* as it bypasses type safety, but can be used as an escape hatch for prototyping.
|
|
8
8
|
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* const result = T.any.validate(anything) // Returns the value as any
|
|
12
|
+
* // result is typed as any - use with caution!
|
|
13
|
+
* ```
|
|
9
14
|
* @public
|
|
10
15
|
*/
|
|
11
16
|
declare const any: Validator<any>;
|
|
12
17
|
|
|
13
18
|
/**
|
|
14
|
-
*
|
|
19
|
+
* Validator that ensures a value is an array. Does not validate the contents of the array.
|
|
20
|
+
* Use T.arrayOf() to validate both the array structure and its contents.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* const items = T.array.validate([1, "hello", true]) // Returns unknown[]
|
|
25
|
+
* T.array.validate("not array") // Throws ValidationError: "Expected an array, got a string"
|
|
15
26
|
*
|
|
27
|
+
* // For typed arrays, use T.arrayOf:
|
|
28
|
+
* const numbers = T.arrayOf(T.number).validate([1, 2, 3]) // Returns number[]
|
|
29
|
+
* ```
|
|
16
30
|
* @public
|
|
17
31
|
*/
|
|
18
32
|
declare const array: Validator<unknown[]>;
|
|
19
33
|
|
|
20
34
|
/**
|
|
21
|
-
*
|
|
35
|
+
* Creates a validator for arrays where each element is validated using the provided validator.
|
|
36
|
+
*
|
|
37
|
+
* @param itemValidator - Validator to use for each array element
|
|
38
|
+
* @returns An ArrayOfValidator that validates both array structure and element types
|
|
39
|
+
* @throws ValidationError When the value is not an array or when any element is invalid
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* const numberArray = T.arrayOf(T.number)
|
|
43
|
+
* numberArray.validate([1, 2, 3]) // Returns number[]
|
|
44
|
+
* numberArray.validate([1, "2", 3]) // Throws ValidationError at index 1
|
|
22
45
|
*
|
|
46
|
+
* const userArray = T.arrayOf(T.object({ name: T.string, age: T.number }))
|
|
47
|
+
* ```
|
|
23
48
|
* @public
|
|
24
49
|
*/
|
|
25
50
|
declare function arrayOf<T>(itemValidator: Validatable<T>): ArrayOfValidator<T>;
|
|
26
51
|
|
|
27
|
-
/**
|
|
52
|
+
/**
|
|
53
|
+
* Validator for arrays where each element is validated using the provided item validator.
|
|
54
|
+
* Extends the base Validator class with array-specific validation methods.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```ts
|
|
58
|
+
* const stringArray = new ArrayOfValidator(T.string)
|
|
59
|
+
* const numbers = stringArray.validate(["a", "b", "c"]) // Returns string[]
|
|
60
|
+
*
|
|
61
|
+
* const userArray = T.arrayOf(T.object({ name: T.string, age: T.number }))
|
|
62
|
+
* ```
|
|
63
|
+
* @public
|
|
64
|
+
*/
|
|
28
65
|
export declare class ArrayOfValidator<T> extends Validator<T[]> {
|
|
29
66
|
readonly itemValidator: Validatable<T>;
|
|
67
|
+
/**
|
|
68
|
+
* Creates a new ArrayOfValidator.
|
|
69
|
+
*
|
|
70
|
+
* itemValidator - Validator used to validate each array element
|
|
71
|
+
*/
|
|
30
72
|
constructor(itemValidator: Validatable<T>);
|
|
73
|
+
/**
|
|
74
|
+
* Returns a new validator that ensures the array is not empty.
|
|
75
|
+
*
|
|
76
|
+
* @returns A new validator that rejects empty arrays
|
|
77
|
+
* @throws ValidationError When the array is empty
|
|
78
|
+
* @example
|
|
79
|
+
* ```ts
|
|
80
|
+
* const nonEmptyStrings = T.arrayOf(T.string).nonEmpty()
|
|
81
|
+
* nonEmptyStrings.validate(["hello"]) // Valid
|
|
82
|
+
* nonEmptyStrings.validate([]) // Throws ValidationError
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
31
85
|
nonEmpty(): Validator<T[]>;
|
|
86
|
+
/**
|
|
87
|
+
* Returns a new validator that ensures the array has more than one element.
|
|
88
|
+
*
|
|
89
|
+
* @returns A new validator that requires at least 2 elements
|
|
90
|
+
* @throws ValidationError When the array has 1 or fewer elements
|
|
91
|
+
* @example
|
|
92
|
+
* ```ts
|
|
93
|
+
* const multipleItems = T.arrayOf(T.string).lengthGreaterThan1()
|
|
94
|
+
* multipleItems.validate(["a", "b"]) // Valid
|
|
95
|
+
* multipleItems.validate(["a"]) // Throws ValidationError
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
32
98
|
lengthGreaterThan1(): Validator<T[]>;
|
|
33
99
|
}
|
|
34
100
|
|
|
35
101
|
/**
|
|
36
|
-
*
|
|
102
|
+
* Validator that ensures a value is a bigint.
|
|
37
103
|
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```ts
|
|
106
|
+
* const largeNumber = T.bigint.validate(123n) // Returns 123n
|
|
107
|
+
* T.bigint.validate(123) // Throws ValidationError: "Expected bigint, got a number"
|
|
108
|
+
* ```
|
|
38
109
|
* @public
|
|
39
110
|
*/
|
|
40
111
|
declare const bigint: Validator<bigint>;
|
|
41
112
|
|
|
42
113
|
/**
|
|
43
|
-
*
|
|
114
|
+
* Validator that ensures a value is a boolean.
|
|
44
115
|
*
|
|
116
|
+
* @example
|
|
117
|
+
* ```ts
|
|
118
|
+
* const isActive = T.boolean.validate(true) // Returns true
|
|
119
|
+
* const isEnabled = T.boolean.validate(false) // Returns false
|
|
120
|
+
* T.boolean.validate("true") // Throws ValidationError: "Expected boolean, got a string"
|
|
121
|
+
* ```
|
|
45
122
|
* @public
|
|
46
123
|
*/
|
|
47
124
|
declare const boolean: Validator<boolean>;
|
|
48
125
|
|
|
49
126
|
/**
|
|
50
|
-
*
|
|
127
|
+
* Creates a validator for dictionary objects where both keys and values are validated.
|
|
128
|
+
* Useful for validating objects used as key-value maps.
|
|
129
|
+
*
|
|
130
|
+
* @param keyValidator - Validator for object keys
|
|
131
|
+
* @param valueValidator - Validator for object values
|
|
132
|
+
* @returns A DictValidator that validates all keys and values
|
|
133
|
+
* @throws ValidationError When any key or value is invalid
|
|
134
|
+
* @example
|
|
135
|
+
* ```ts
|
|
136
|
+
* const scores = T.dict(T.string, T.number)
|
|
137
|
+
* scores.validate({ "alice": 100, "bob": 85 }) // Valid
|
|
51
138
|
*
|
|
139
|
+
* const userPrefs = T.dict(T.string, T.object({
|
|
140
|
+
* theme: T.literalEnum('light', 'dark'),
|
|
141
|
+
* notifications: T.boolean
|
|
142
|
+
* }))
|
|
143
|
+
* ```
|
|
52
144
|
* @public
|
|
53
145
|
*/
|
|
54
146
|
declare function dict<Key extends string, Value>(keyValidator: Validatable<Key>, valueValidator: Validatable<Value>): DictValidator<Key, Value>;
|
|
55
147
|
|
|
56
|
-
/**
|
|
148
|
+
/**
|
|
149
|
+
* Validator for dictionary/map objects where both keys and values are validated.
|
|
150
|
+
* Useful for validating objects used as key-value stores.
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```ts
|
|
154
|
+
* const scoreDict = new DictValidator(T.string, T.number)
|
|
155
|
+
* const scores = scoreDict.validate({
|
|
156
|
+
* "alice": 100,
|
|
157
|
+
* "bob": 85,
|
|
158
|
+
* "charlie": 92
|
|
159
|
+
* })
|
|
160
|
+
* // scores is typed as Record<string, number>
|
|
161
|
+
* ```
|
|
162
|
+
* @public
|
|
163
|
+
*/
|
|
57
164
|
export declare class DictValidator<Key extends string, Value> extends Validator<Record<Key, Value>> {
|
|
58
165
|
readonly keyValidator: Validatable<Key>;
|
|
59
166
|
readonly valueValidator: Validatable<Value>;
|
|
167
|
+
/**
|
|
168
|
+
* Creates a new DictValidator.
|
|
169
|
+
*
|
|
170
|
+
* keyValidator - Validator for object keys
|
|
171
|
+
* valueValidator - Validator for object values
|
|
172
|
+
*/
|
|
60
173
|
constructor(keyValidator: Validatable<Key>, valueValidator: Validatable<Value>);
|
|
61
174
|
}
|
|
62
175
|
|
|
63
176
|
/**
|
|
64
|
-
*
|
|
177
|
+
* Validator for HTTP and HTTPS URLs only. Rejects all other protocols.
|
|
65
178
|
*
|
|
179
|
+
* @example
|
|
180
|
+
* ```ts
|
|
181
|
+
* const apiUrl = T.httpUrl.validate("https://api.example.com") // Valid
|
|
182
|
+
* const httpUrl = T.httpUrl.validate("http://localhost:3000") // Valid
|
|
183
|
+
* T.httpUrl.validate("") // Valid (empty string allowed)
|
|
184
|
+
* T.httpUrl.validate("ftp://files.example.com") // Throws ValidationError (not http/https)
|
|
185
|
+
* ```
|
|
66
186
|
* @public
|
|
67
187
|
*/
|
|
68
188
|
declare const httpUrl: Validator<string>;
|
|
69
189
|
|
|
70
190
|
/**
|
|
71
|
-
*
|
|
191
|
+
* Validator for IndexKey values used in tldraw's indexing system. An IndexKey is a string
|
|
192
|
+
* that meets specific format requirements for use as a database index.
|
|
193
|
+
*
|
|
194
|
+
* @throws ValidationError When the string is not a valid IndexKey format
|
|
195
|
+
* @example
|
|
196
|
+
* ```ts
|
|
197
|
+
* const key = T.indexKey.validate("valid_index_key") // Returns IndexKey
|
|
198
|
+
* T.indexKey.validate("invalid key!") // Throws ValidationError (invalid format)
|
|
199
|
+
* ```
|
|
72
200
|
* @public
|
|
73
201
|
*/
|
|
74
202
|
declare const indexKey: Validator<IndexKey>;
|
|
75
203
|
|
|
76
204
|
/**
|
|
77
|
-
*
|
|
205
|
+
* Validator that ensures a value is an integer (whole number).
|
|
78
206
|
*
|
|
207
|
+
* @example
|
|
208
|
+
* ```ts
|
|
209
|
+
* const count = T.integer.validate(42) // Returns 42
|
|
210
|
+
* T.integer.validate(3.14) // Throws ValidationError: "Expected an integer, got 3.14"
|
|
211
|
+
* T.integer.validate(-5) // Returns -5 (negative integers are valid)
|
|
212
|
+
* ```
|
|
79
213
|
* @public
|
|
80
214
|
*/
|
|
81
215
|
declare const integer: Validator<number>;
|
|
82
216
|
|
|
83
217
|
/**
|
|
84
|
-
*
|
|
218
|
+
* Creates a validator for JSON dictionaries (objects with string keys and JSON-serializable values).
|
|
85
219
|
*
|
|
220
|
+
* @returns A DictValidator that validates string keys and JSON values
|
|
221
|
+
* @throws ValidationError When keys are not strings or values are not JSON-serializable
|
|
222
|
+
* @example
|
|
223
|
+
* ```ts
|
|
224
|
+
* const config = T.jsonDict().validate({
|
|
225
|
+
* "setting1": "value",
|
|
226
|
+
* "setting2": 42,
|
|
227
|
+
* "setting3": ["a", "b", "c"],
|
|
228
|
+
* "setting4": { nested: true }
|
|
229
|
+
* })
|
|
230
|
+
* ```
|
|
86
231
|
* @public
|
|
87
232
|
*/
|
|
88
233
|
declare function jsonDict(): DictValidator<string, JsonValue>;
|
|
89
234
|
|
|
90
235
|
/**
|
|
91
|
-
*
|
|
236
|
+
* Validator that ensures a value is valid JSON (string, number, boolean, null, array, or plain object).
|
|
237
|
+
* Rejects functions, undefined, symbols, and other non-JSON values.
|
|
92
238
|
*
|
|
239
|
+
* @example
|
|
240
|
+
* ```ts
|
|
241
|
+
* const data = T.jsonValue.validate({ name: "Alice", scores: [1, 2, 3], active: true })
|
|
242
|
+
* T.jsonValue.validate(undefined) // Throws ValidationError
|
|
243
|
+
* T.jsonValue.validate(() => {}) // Throws ValidationError
|
|
244
|
+
* ```
|
|
93
245
|
* @public
|
|
94
246
|
*/
|
|
95
247
|
declare const jsonValue: Validator<JsonValue>;
|
|
96
248
|
|
|
97
249
|
/**
|
|
98
|
-
*
|
|
250
|
+
* Validator for URLs that are safe to use as user-facing links. Accepts http, https, and mailto protocols.
|
|
251
|
+
* This validator provides security by rejecting potentially dangerous protocols like javascript:.
|
|
99
252
|
*
|
|
253
|
+
* @example
|
|
254
|
+
* ```ts
|
|
255
|
+
* const link = T.linkUrl.validate("https://example.com") // Valid
|
|
256
|
+
* const email = T.linkUrl.validate("mailto:user@example.com") // Valid
|
|
257
|
+
* T.linkUrl.validate("") // Valid (empty string allowed)
|
|
258
|
+
* T.linkUrl.validate("javascript:alert(1)") // Throws ValidationError (unsafe protocol)
|
|
259
|
+
* ```
|
|
100
260
|
* @public
|
|
101
261
|
*/
|
|
102
262
|
declare const linkUrl: Validator<string>;
|
|
103
263
|
|
|
104
264
|
/**
|
|
105
|
-
*
|
|
265
|
+
* Creates a validator that only accepts a specific literal value.
|
|
106
266
|
*
|
|
267
|
+
* @param expectedValue - The exact value that must be matched
|
|
268
|
+
* @returns A validator that only accepts the specified literal value
|
|
269
|
+
* @throws ValidationError When the value doesn't match the expected literal
|
|
107
270
|
* @example
|
|
108
|
-
*
|
|
109
271
|
* ```ts
|
|
110
272
|
* const trueValidator = T.literal(true)
|
|
111
|
-
*
|
|
273
|
+
* trueValidator.validate(true) // Returns true
|
|
274
|
+
* trueValidator.validate(false) // Throws ValidationError
|
|
112
275
|
*
|
|
276
|
+
* const statusValidator = T.literal("active")
|
|
277
|
+
* statusValidator.validate("active") // Returns "active"
|
|
278
|
+
* statusValidator.validate("inactive") // Throws ValidationError
|
|
279
|
+
* ```
|
|
113
280
|
* @public
|
|
114
281
|
*/
|
|
115
282
|
declare function literal<T extends boolean | number | string>(expectedValue: T): Validator<T>;
|
|
116
283
|
|
|
117
|
-
/**
|
|
284
|
+
/**
|
|
285
|
+
* Creates a validator that only accepts one of the provided literal values.
|
|
286
|
+
* This is a convenience function that creates a setEnum from the provided values.
|
|
287
|
+
*
|
|
288
|
+
* @param values - The allowed literal values
|
|
289
|
+
* @returns A validator that only accepts the provided literal values
|
|
290
|
+
* @throws ValidationError When the value is not one of the allowed literals
|
|
291
|
+
* @example
|
|
292
|
+
* ```ts
|
|
293
|
+
* const themeValidator = T.literalEnum('light', 'dark', 'auto')
|
|
294
|
+
* themeValidator.validate('light') // Returns 'light'
|
|
295
|
+
* themeValidator.validate('blue') // Throws ValidationError: Expected "light" or "dark" or "auto", got blue
|
|
296
|
+
* ```
|
|
297
|
+
* @public
|
|
298
|
+
*/
|
|
118
299
|
declare function literalEnum<const Values extends readonly unknown[]>(...values: Values): Validator<Values[number]>;
|
|
119
300
|
|
|
120
301
|
/**
|
|
121
|
-
*
|
|
122
|
-
*
|
|
302
|
+
* Creates a validator for named model objects with enhanced error reporting. The model name
|
|
303
|
+
* will be included in error messages to provide better debugging context.
|
|
304
|
+
*
|
|
305
|
+
* @param name - The name of the model (used in error messages)
|
|
306
|
+
* @param validator - The validator for the model structure
|
|
307
|
+
* @returns A Validator with enhanced error reporting that includes the model name
|
|
308
|
+
* @throws ValidationError With model name context when validation fails
|
|
309
|
+
* @example
|
|
310
|
+
* ```ts
|
|
311
|
+
* const userModel = T.model('User', T.object({
|
|
312
|
+
* id: T.string,
|
|
313
|
+
* name: T.string,
|
|
314
|
+
* email: T.linkUrl
|
|
315
|
+
* }))
|
|
123
316
|
*
|
|
317
|
+
* // Error message will be: "At User.email: Expected a valid url, got 'invalid-email'"
|
|
318
|
+
* ```
|
|
124
319
|
* @public
|
|
125
320
|
*/
|
|
126
321
|
declare function model<T extends {
|
|
@@ -128,25 +323,56 @@ declare function model<T extends {
|
|
|
128
323
|
}>(name: string, validator: Validatable<T>): Validator<T>;
|
|
129
324
|
|
|
130
325
|
/**
|
|
131
|
-
*
|
|
326
|
+
* Validator that ensures a value is a positive integer (\> 0). Rejects zero and negative integers.
|
|
132
327
|
*
|
|
328
|
+
* @example
|
|
329
|
+
* ```ts
|
|
330
|
+
* const itemCount = T.nonZeroInteger.validate(1) // Returns 1
|
|
331
|
+
* T.nonZeroInteger.validate(0) // Throws ValidationError: "Expected a non-zero positive integer, got 0"
|
|
332
|
+
* T.nonZeroInteger.validate(-5) // Throws ValidationError: "Expected a non-zero positive integer, got -5"
|
|
333
|
+
* ```
|
|
133
334
|
* @public
|
|
134
335
|
*/
|
|
135
336
|
declare const nonZeroInteger: Validator<number>;
|
|
136
337
|
|
|
137
338
|
/**
|
|
138
|
-
*
|
|
339
|
+
* Validator that ensures a value is a positive number (\> 0). Rejects zero and negative numbers.
|
|
139
340
|
*
|
|
341
|
+
* @example
|
|
342
|
+
* ```ts
|
|
343
|
+
* const quantity = T.nonZeroNumber.validate(0.01) // Returns 0.01
|
|
344
|
+
* T.nonZeroNumber.validate(0) // Throws ValidationError: "Expected a non-zero positive number, got 0"
|
|
345
|
+
* T.nonZeroNumber.validate(-5) // Throws ValidationError: "Expected a non-zero positive number, got -5"
|
|
346
|
+
* ```
|
|
140
347
|
* @public
|
|
141
348
|
*/
|
|
142
349
|
declare const nonZeroNumber: Validator<number>;
|
|
143
350
|
|
|
144
|
-
/**
|
|
351
|
+
/**
|
|
352
|
+
* Creates a validator that accepts either the validated type or null.
|
|
353
|
+
*
|
|
354
|
+
* @param validator - The base validator to make nullable
|
|
355
|
+
* @returns A validator that accepts T or null
|
|
356
|
+
* @example
|
|
357
|
+
* ```ts
|
|
358
|
+
* const nullableString = T.nullable(T.string)
|
|
359
|
+
* nullableString.validate("hello") // Returns "hello"
|
|
360
|
+
* nullableString.validate(null) // Returns null
|
|
361
|
+
* nullableString.validate(undefined) // Throws ValidationError
|
|
362
|
+
* ```
|
|
363
|
+
* @public
|
|
364
|
+
*/
|
|
145
365
|
declare function nullable<T>(validator: Validatable<T>): Validator<null | T>;
|
|
146
366
|
|
|
147
367
|
/**
|
|
148
|
-
*
|
|
368
|
+
* Validator that ensures a value is a finite, non-NaN number. Rejects Infinity, -Infinity, and NaN.
|
|
149
369
|
*
|
|
370
|
+
* @example
|
|
371
|
+
* ```ts
|
|
372
|
+
* const count = T.number.validate(42) // Returns 42 as number
|
|
373
|
+
* T.number.validate(NaN) // Throws ValidationError: "Expected a number, got NaN"
|
|
374
|
+
* T.number.validate(Infinity) // Throws ValidationError: "Expected a finite number, got Infinity"
|
|
375
|
+
* ```
|
|
150
376
|
* @public
|
|
151
377
|
*/
|
|
152
378
|
declare const number: Validator<number>;
|
|
@@ -154,36 +380,93 @@ declare const number: Validator<number>;
|
|
|
154
380
|
/* Excluded from this release type: numberUnion */
|
|
155
381
|
|
|
156
382
|
/**
|
|
157
|
-
*
|
|
383
|
+
* Creates a validator for objects with a defined shape. Each property is validated using
|
|
384
|
+
* its corresponding validator from the config object.
|
|
158
385
|
*
|
|
386
|
+
* @param config - Object mapping property names to their validators
|
|
387
|
+
* @returns An ObjectValidator that validates the object structure and all properties
|
|
388
|
+
* @throws ValidationError When the value is not an object or when any property is invalid
|
|
389
|
+
* @example
|
|
390
|
+
* ```ts
|
|
391
|
+
* const userValidator = T.object({
|
|
392
|
+
* name: T.string,
|
|
393
|
+
* age: T.number,
|
|
394
|
+
* email: T.string.optional(),
|
|
395
|
+
* isActive: T.boolean
|
|
396
|
+
* })
|
|
397
|
+
*
|
|
398
|
+
* const user = userValidator.validate({
|
|
399
|
+
* name: "Alice",
|
|
400
|
+
* age: 25,
|
|
401
|
+
* email: "alice@example.com",
|
|
402
|
+
* isActive: true
|
|
403
|
+
* })
|
|
404
|
+
* // user is typed with full type safety
|
|
405
|
+
* ```
|
|
159
406
|
* @public
|
|
160
407
|
*/
|
|
161
408
|
declare function object<Shape extends object>(config: {
|
|
162
409
|
readonly [K in keyof Shape]: Validatable<Shape[K]>;
|
|
163
410
|
}): ObjectValidator<MakeUndefinedOptional<Shape>>;
|
|
164
411
|
|
|
165
|
-
/**
|
|
412
|
+
/**
|
|
413
|
+
* Validator for objects with a defined shape. Each property is validated using its corresponding
|
|
414
|
+
* validator from the config object. Can be configured to allow or reject unknown properties.
|
|
415
|
+
*
|
|
416
|
+
* @example
|
|
417
|
+
* ```ts
|
|
418
|
+
* const userValidator = new ObjectValidator({
|
|
419
|
+
* name: T.string,
|
|
420
|
+
* age: T.number,
|
|
421
|
+
* email: T.string.optional()
|
|
422
|
+
* })
|
|
423
|
+
*
|
|
424
|
+
* const user = userValidator.validate({
|
|
425
|
+
* name: "Alice",
|
|
426
|
+
* age: 25,
|
|
427
|
+
* email: "alice@example.com"
|
|
428
|
+
* })
|
|
429
|
+
* ```
|
|
430
|
+
* @public
|
|
431
|
+
*/
|
|
166
432
|
export declare class ObjectValidator<Shape extends object> extends Validator<Shape> {
|
|
167
433
|
readonly config: {
|
|
168
434
|
readonly [K in keyof Shape]: Validatable<Shape[K]>;
|
|
169
435
|
};
|
|
170
436
|
private readonly shouldAllowUnknownProperties;
|
|
437
|
+
/**
|
|
438
|
+
* Creates a new ObjectValidator.
|
|
439
|
+
*
|
|
440
|
+
* config - Object mapping property names to their validators
|
|
441
|
+
* shouldAllowUnknownProperties - Whether to allow properties not defined in config
|
|
442
|
+
*/
|
|
171
443
|
constructor(config: {
|
|
172
444
|
readonly [K in keyof Shape]: Validatable<Shape[K]>;
|
|
173
445
|
}, shouldAllowUnknownProperties?: boolean);
|
|
174
|
-
allowUnknownProperties(): ObjectValidator<Shape>;
|
|
175
446
|
/**
|
|
176
|
-
*
|
|
447
|
+
* Returns a new validator that allows unknown properties in the validated object.
|
|
177
448
|
*
|
|
449
|
+
* @returns A new ObjectValidator that accepts extra properties
|
|
178
450
|
* @example
|
|
451
|
+
* ```ts
|
|
452
|
+
* const flexibleUser = T.object({ name: T.string }).allowUnknownProperties()
|
|
453
|
+
* flexibleUser.validate({ name: "Alice", extra: "allowed" }) // Valid
|
|
454
|
+
* ```
|
|
455
|
+
*/
|
|
456
|
+
allowUnknownProperties(): ObjectValidator<Shape>;
|
|
457
|
+
/**
|
|
458
|
+
* Creates a new ObjectValidator by extending this validator with additional properties.
|
|
179
459
|
*
|
|
460
|
+
* @param extension - Object mapping new property names to their validators
|
|
461
|
+
* @returns A new ObjectValidator that validates both original and extended properties
|
|
462
|
+
* @example
|
|
180
463
|
* ```ts
|
|
181
|
-
* const
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
* meowVolume: T.number,
|
|
464
|
+
* const baseUser = T.object({ name: T.string, age: T.number })
|
|
465
|
+
* const adminUser = baseUser.extend({
|
|
466
|
+
* permissions: T.arrayOf(T.string),
|
|
467
|
+
* isAdmin: T.boolean
|
|
186
468
|
* })
|
|
469
|
+
* // adminUser validates: { name: string; age: number; permissions: string[]; isAdmin: boolean }
|
|
187
470
|
* ```
|
|
188
471
|
*/
|
|
189
472
|
extend<Extension extends Record<string, unknown>>(extension: {
|
|
@@ -191,43 +474,110 @@ export declare class ObjectValidator<Shape extends object> extends Validator<Sha
|
|
|
191
474
|
}): ObjectValidator<Shape & Extension>;
|
|
192
475
|
}
|
|
193
476
|
|
|
194
|
-
/**
|
|
477
|
+
/**
|
|
478
|
+
* Creates a validator that accepts either the validated type or undefined.
|
|
479
|
+
*
|
|
480
|
+
* @param validator - The base validator to make optional
|
|
481
|
+
* @returns A validator that accepts T or undefined
|
|
482
|
+
* @example
|
|
483
|
+
* ```ts
|
|
484
|
+
* const optionalString = T.optional(T.string)
|
|
485
|
+
* optionalString.validate("hello") // Returns "hello"
|
|
486
|
+
* optionalString.validate(undefined) // Returns undefined
|
|
487
|
+
* optionalString.validate(null) // Throws ValidationError
|
|
488
|
+
* ```
|
|
489
|
+
* @public
|
|
490
|
+
*/
|
|
195
491
|
declare function optional<T>(validator: Validatable<T>): Validator<T | undefined>;
|
|
196
492
|
|
|
197
493
|
/**
|
|
198
|
-
*
|
|
494
|
+
* Creates a validator that accepts values matching either of two validators.
|
|
495
|
+
* Tries the first validator, and if it fails, tries the second validator.
|
|
199
496
|
*
|
|
497
|
+
* @param v1 - The first validator to try
|
|
498
|
+
* @param v2 - The second validator to try if the first fails
|
|
499
|
+
* @returns A validator that accepts values matching either validator
|
|
500
|
+
* @throws ValidationError When the value matches neither validator (throws error from v2)
|
|
501
|
+
* @example
|
|
502
|
+
* ```ts
|
|
503
|
+
* const stringOrNumber = T.or(T.string, T.number)
|
|
504
|
+
* stringOrNumber.validate("hello") // Returns "hello" as string
|
|
505
|
+
* stringOrNumber.validate(42) // Returns 42 as number
|
|
506
|
+
* stringOrNumber.validate(true) // Throws ValidationError from number validator
|
|
507
|
+
* ```
|
|
200
508
|
* @public
|
|
201
509
|
*/
|
|
202
510
|
declare function or<T1, T2>(v1: Validatable<T1>, v2: Validatable<T2>): Validator<T1 | T2>;
|
|
203
511
|
|
|
204
512
|
/**
|
|
205
|
-
*
|
|
513
|
+
* Validator that ensures a value is a non-negative integer (\>= 0).
|
|
514
|
+
* Despite the name "positive", this validator accepts zero.
|
|
206
515
|
*
|
|
516
|
+
* @example
|
|
517
|
+
* ```ts
|
|
518
|
+
* const index = T.positiveInteger.validate(5) // Returns 5
|
|
519
|
+
* const start = T.positiveInteger.validate(0) // Returns 0 (valid)
|
|
520
|
+
* T.positiveInteger.validate(-1) // Throws ValidationError: "Expected a positive integer, got -1"
|
|
521
|
+
* T.positiveInteger.validate(3.14) // Throws ValidationError: "Expected an integer, got 3.14"
|
|
522
|
+
* ```
|
|
207
523
|
* @public
|
|
208
524
|
*/
|
|
209
525
|
declare const positiveInteger: Validator<number>;
|
|
210
526
|
|
|
211
527
|
/**
|
|
212
|
-
*
|
|
528
|
+
* Validator that ensures a value is a non-negative number (\>= 0).
|
|
529
|
+
* Despite the name "positive", this validator accepts zero.
|
|
213
530
|
*
|
|
531
|
+
* @example
|
|
532
|
+
* ```ts
|
|
533
|
+
* const price = T.positiveNumber.validate(29.99) // Returns 29.99
|
|
534
|
+
* const free = T.positiveNumber.validate(0) // Returns 0 (valid)
|
|
535
|
+
* T.positiveNumber.validate(-1) // Throws ValidationError: "Expected a positive number, got -1"
|
|
536
|
+
* ```
|
|
214
537
|
* @public
|
|
215
538
|
*/
|
|
216
539
|
declare const positiveNumber: Validator<number>;
|
|
217
540
|
|
|
218
|
-
/**
|
|
541
|
+
/**
|
|
542
|
+
* Creates a validator that only accepts values from a given Set of allowed values.
|
|
543
|
+
*
|
|
544
|
+
* @param values - Set containing the allowed values
|
|
545
|
+
* @returns A validator that only accepts values from the provided set
|
|
546
|
+
* @throws ValidationError When the value is not in the allowed set
|
|
547
|
+
* @example
|
|
548
|
+
* ```ts
|
|
549
|
+
* const allowedColors = new Set(['red', 'green', 'blue'] as const)
|
|
550
|
+
* const colorValidator = T.setEnum(allowedColors)
|
|
551
|
+
* colorValidator.validate('red') // Returns 'red'
|
|
552
|
+
* colorValidator.validate('yellow') // Throws ValidationError
|
|
553
|
+
* ```
|
|
554
|
+
* @public
|
|
555
|
+
*/
|
|
219
556
|
declare function setEnum<T>(values: ReadonlySet<T>): Validator<T>;
|
|
220
557
|
|
|
221
558
|
/**
|
|
222
|
-
*
|
|
559
|
+
* Validator for URLs that are safe to use as asset sources. Accepts http, https, data, and asset protocols.
|
|
560
|
+
* The asset: protocol refers to tldraw's local IndexedDB object store.
|
|
223
561
|
*
|
|
562
|
+
* @example
|
|
563
|
+
* ```ts
|
|
564
|
+
* const imageUrl = T.srcUrl.validate("https://example.com/image.png") // Valid
|
|
565
|
+
* const dataUrl = T.srcUrl.validate("data:image/png;base64,iVBORw0...") // Valid
|
|
566
|
+
* const assetUrl = T.srcUrl.validate("asset:abc123") // Valid (local asset reference)
|
|
567
|
+
* T.srcUrl.validate("") // Valid (empty string allowed)
|
|
568
|
+
* ```
|
|
224
569
|
* @public
|
|
225
570
|
*/
|
|
226
571
|
declare const srcUrl: Validator<string>;
|
|
227
572
|
|
|
228
573
|
/**
|
|
229
|
-
*
|
|
574
|
+
* Validator that ensures a value is a string.
|
|
230
575
|
*
|
|
576
|
+
* @example
|
|
577
|
+
* ```ts
|
|
578
|
+
* const name = T.string.validate("hello") // Returns "hello" as string
|
|
579
|
+
* T.string.validate(123) // Throws ValidationError: "Expected string, got a number"
|
|
580
|
+
* ```
|
|
231
581
|
* @public
|
|
232
582
|
*/
|
|
233
583
|
declare const string: Validator<string>;
|
|
@@ -279,38 +629,104 @@ declare namespace T {
|
|
|
279
629
|
}
|
|
280
630
|
export { T }
|
|
281
631
|
|
|
282
|
-
/**
|
|
632
|
+
/**
|
|
633
|
+
* Utility type that extracts the validated type from a Validatable object.
|
|
634
|
+
* Useful for deriving TypeScript types from validator definitions.
|
|
635
|
+
*
|
|
636
|
+
* @example
|
|
637
|
+
* ```ts
|
|
638
|
+
* const userValidator = T.object({ name: T.string, age: T.number })
|
|
639
|
+
* type User = TypeOf<typeof userValidator> // { name: string; age: number }
|
|
640
|
+
* ```
|
|
641
|
+
* @public
|
|
642
|
+
*/
|
|
283
643
|
declare type TypeOf<V extends Validatable<any>> = V extends Validatable<infer T> ? T : never;
|
|
284
644
|
|
|
285
645
|
/**
|
|
286
|
-
*
|
|
287
|
-
*
|
|
646
|
+
* Creates a validator for discriminated union types. Validates objects that can be one of
|
|
647
|
+
* several variants, distinguished by a discriminator property.
|
|
288
648
|
*
|
|
649
|
+
* @param key - The discriminator property name used to determine the variant
|
|
650
|
+
* @param config - Object mapping variant names to their validators
|
|
651
|
+
* @returns A UnionValidator that validates based on the discriminator value
|
|
652
|
+
* @throws ValidationError When the discriminator is invalid or the variant validation fails
|
|
289
653
|
* @example
|
|
290
|
-
*
|
|
291
654
|
* ```ts
|
|
292
|
-
* const
|
|
293
|
-
*
|
|
294
|
-
*
|
|
295
|
-
*
|
|
655
|
+
* const shapeValidator = T.union('type', {
|
|
656
|
+
* circle: T.object({ type: T.literal('circle'), radius: T.number }),
|
|
657
|
+
* square: T.object({ type: T.literal('square'), size: T.number }),
|
|
658
|
+
* triangle: T.object({ type: T.literal('triangle'), base: T.number, height: T.number })
|
|
659
|
+
* })
|
|
296
660
|
*
|
|
661
|
+
* const circle = shapeValidator.validate({ type: 'circle', radius: 5 })
|
|
662
|
+
* // circle is typed as { type: 'circle'; radius: number }
|
|
663
|
+
* ```
|
|
297
664
|
* @public
|
|
298
665
|
*/
|
|
299
666
|
declare function union<Key extends string, Config extends UnionValidatorConfig<Key, Config>>(key: Key, config: Config): UnionValidator<Key, Config>;
|
|
300
667
|
|
|
301
|
-
/**
|
|
668
|
+
/**
|
|
669
|
+
* Validator for discriminated union types. Validates objects that can be one of several variants,
|
|
670
|
+
* distinguished by a discriminator property (key) that indicates which variant the object represents.
|
|
671
|
+
*
|
|
672
|
+
* @example
|
|
673
|
+
* ```ts
|
|
674
|
+
* const shapeValidator = new UnionValidator('type', {
|
|
675
|
+
* circle: T.object({ type: T.literal('circle'), radius: T.number }),
|
|
676
|
+
* square: T.object({ type: T.literal('square'), size: T.number })
|
|
677
|
+
* }, () => { throw new Error('Unknown shape') }, false)
|
|
678
|
+
*
|
|
679
|
+
* const circle = shapeValidator.validate({ type: 'circle', radius: 5 })
|
|
680
|
+
* // circle is typed as { type: 'circle'; radius: number }
|
|
681
|
+
* ```
|
|
682
|
+
* @public
|
|
683
|
+
*/
|
|
302
684
|
export declare class UnionValidator<Key extends string, Config extends UnionValidatorConfig<Key, Config>, UnknownValue = never> extends Validator<TypeOf<Config[keyof Config]> | UnknownValue> {
|
|
303
685
|
private readonly key;
|
|
304
686
|
private readonly config;
|
|
305
687
|
private readonly unknownValueValidation;
|
|
306
688
|
private readonly useNumberKeys;
|
|
689
|
+
/**
|
|
690
|
+
* Creates a new UnionValidator.
|
|
691
|
+
*
|
|
692
|
+
* key - The discriminator property name used to determine the variant
|
|
693
|
+
* config - Object mapping variant names to their validators
|
|
694
|
+
* unknownValueValidation - Function to handle unknown variants
|
|
695
|
+
* useNumberKeys - Whether the discriminator uses number keys instead of strings
|
|
696
|
+
*/
|
|
307
697
|
constructor(key: Key, config: Config, unknownValueValidation: (value: object, variant: string) => UnknownValue, useNumberKeys: boolean);
|
|
308
698
|
private expectObject;
|
|
309
699
|
private getMatchingSchemaAndVariant;
|
|
700
|
+
/**
|
|
701
|
+
* Returns a new UnionValidator that can handle unknown variants using the provided function.
|
|
702
|
+
*
|
|
703
|
+
* @param unknownValueValidation - Function to validate/transform unknown variants
|
|
704
|
+
* @returns A new UnionValidator that accepts unknown variants
|
|
705
|
+
* @example
|
|
706
|
+
* ```ts
|
|
707
|
+
* const shapeValidator = T.union('type', { circle: circleValidator })
|
|
708
|
+
* .validateUnknownVariants((obj, variant) => {
|
|
709
|
+
* console.warn(`Unknown shape type: ${variant}`)
|
|
710
|
+
* return obj as UnknownShape
|
|
711
|
+
* })
|
|
712
|
+
* ```
|
|
713
|
+
*/
|
|
310
714
|
validateUnknownVariants<Unknown>(unknownValueValidation: (value: object, variant: string) => Unknown): UnionValidator<Key, Config, Unknown>;
|
|
311
715
|
}
|
|
312
716
|
|
|
313
|
-
/**
|
|
717
|
+
/**
|
|
718
|
+
* Configuration type for union validators. Each variant must be a validator that produces
|
|
719
|
+
* an object with the discriminator key set to the variant name.
|
|
720
|
+
*
|
|
721
|
+
* @example
|
|
722
|
+
* ```ts
|
|
723
|
+
* type ShapeConfig = UnionValidatorConfig<'type', {
|
|
724
|
+
* circle: Validatable<{ type: 'circle'; radius: number }>
|
|
725
|
+
* square: Validatable<{ type: 'square'; size: number }>
|
|
726
|
+
* }>
|
|
727
|
+
* ```
|
|
728
|
+
* @public
|
|
729
|
+
*/
|
|
314
730
|
export declare type UnionValidatorConfig<Key extends string, Config> = {
|
|
315
731
|
readonly [Variant in keyof Config]: Validatable<any> & {
|
|
316
732
|
validate(input: any): {
|
|
@@ -320,88 +736,366 @@ export declare type UnionValidatorConfig<Key extends string, Config> = {
|
|
|
320
736
|
};
|
|
321
737
|
|
|
322
738
|
/**
|
|
323
|
-
*
|
|
324
|
-
* validations.
|
|
739
|
+
* Validator that accepts any value without type checking. Useful as a starting point for
|
|
740
|
+
* building custom validations or when you need to accept truly unknown data.
|
|
325
741
|
*
|
|
742
|
+
* @example
|
|
743
|
+
* ```ts
|
|
744
|
+
* const result = T.unknown.validate(anything) // Returns the value as-is
|
|
745
|
+
* // result is typed as unknown
|
|
746
|
+
* ```
|
|
326
747
|
* @public
|
|
327
748
|
*/
|
|
328
749
|
declare const unknown: Validator<unknown>;
|
|
329
750
|
|
|
330
|
-
/**
|
|
751
|
+
/**
|
|
752
|
+
* Validator that ensures a value is an object (non-null, non-array). Does not validate
|
|
753
|
+
* the properties of the object.
|
|
754
|
+
*
|
|
755
|
+
* @example
|
|
756
|
+
* ```ts
|
|
757
|
+
* const obj = T.unknownObject.validate({ any: "properties" }) // Returns Record<string, unknown>
|
|
758
|
+
* T.unknownObject.validate(null) // Throws ValidationError: "Expected object, got null"
|
|
759
|
+
* T.unknownObject.validate([1, 2, 3]) // Throws ValidationError: "Expected object, got an array"
|
|
760
|
+
* ```
|
|
761
|
+
* @public
|
|
762
|
+
*/
|
|
331
763
|
declare const unknownObject: Validator<Record<string, unknown>>;
|
|
332
764
|
|
|
333
|
-
/**
|
|
765
|
+
/**
|
|
766
|
+
* Interface for objects that can validate unknown values and return typed results.
|
|
767
|
+
* This is the core interface implemented by all validators in the validation system.
|
|
768
|
+
*
|
|
769
|
+
* @example
|
|
770
|
+
* ```ts
|
|
771
|
+
* const customValidator: Validatable<number> = {
|
|
772
|
+
* validate(value) {
|
|
773
|
+
* if (typeof value !== 'number') {
|
|
774
|
+
* throw new ValidationError('Expected number')
|
|
775
|
+
* }
|
|
776
|
+
* return value
|
|
777
|
+
* }
|
|
778
|
+
* }
|
|
779
|
+
* ```
|
|
780
|
+
* @public
|
|
781
|
+
*/
|
|
334
782
|
declare interface Validatable<T> {
|
|
783
|
+
/**
|
|
784
|
+
* Validates an unknown value and returns it with the correct type.
|
|
785
|
+
*
|
|
786
|
+
* @param value - The unknown value to validate
|
|
787
|
+
* @returns The validated value with type T
|
|
788
|
+
* @throws ValidationError When validation fails
|
|
789
|
+
*/
|
|
335
790
|
validate(value: unknown): T;
|
|
336
791
|
/**
|
|
337
|
-
*
|
|
338
|
-
*
|
|
339
|
-
* any part of it has not changed since the last validation.
|
|
792
|
+
* Performance-optimized validation that can use a previously validated value
|
|
793
|
+
* to avoid revalidating unchanged parts of the data structure.
|
|
340
794
|
*
|
|
341
795
|
* If the value has not changed but is not referentially equal, the function
|
|
342
796
|
* should return the previous value.
|
|
343
|
-
*
|
|
797
|
+
*
|
|
798
|
+
* @param knownGoodValue - A previously validated value
|
|
799
|
+
* @param newValue - The new value to validate
|
|
800
|
+
* @returns The validated value, potentially reusing the known good value for performance
|
|
801
|
+
* @throws ValidationError When validation fails
|
|
344
802
|
*/
|
|
345
803
|
validateUsingKnownGoodVersion?(knownGoodValue: T, newValue: unknown): T;
|
|
346
804
|
}
|
|
347
805
|
|
|
348
|
-
/**
|
|
806
|
+
/**
|
|
807
|
+
* Error thrown when validation fails. Provides detailed information about what went wrong
|
|
808
|
+
* and where in the data structure the error occurred.
|
|
809
|
+
*
|
|
810
|
+
* @example
|
|
811
|
+
* ```ts
|
|
812
|
+
* try {
|
|
813
|
+
* validator.validate(invalidData)
|
|
814
|
+
* } catch (error) {
|
|
815
|
+
* if (error instanceof ValidationError) {
|
|
816
|
+
* console.log(error.message) // "At users.0.email: Expected valid URL"
|
|
817
|
+
* console.log(error.path) // ['users', 0, 'email']
|
|
818
|
+
* console.log(error.rawMessage) // "Expected valid URL"
|
|
819
|
+
* }
|
|
820
|
+
* }
|
|
821
|
+
* ```
|
|
822
|
+
* @public
|
|
823
|
+
*/
|
|
349
824
|
declare class ValidationError extends Error {
|
|
350
825
|
readonly rawMessage: string;
|
|
351
826
|
readonly path: ReadonlyArray<number | string>;
|
|
352
827
|
name: string;
|
|
828
|
+
/**
|
|
829
|
+
* Creates a new ValidationError with contextual information about where the error occurred.
|
|
830
|
+
*
|
|
831
|
+
* rawMessage - The raw error message without path information
|
|
832
|
+
* path - Array indicating the location in the data structure where validation failed
|
|
833
|
+
*/
|
|
353
834
|
constructor(rawMessage: string, path?: ReadonlyArray<number | string>);
|
|
354
835
|
}
|
|
355
836
|
|
|
356
|
-
/**
|
|
837
|
+
/**
|
|
838
|
+
* The main validator class that implements the Validatable interface. This is the base class
|
|
839
|
+
* for all validators and provides methods for validation, type checking, and composing validators.
|
|
840
|
+
*
|
|
841
|
+
* @example
|
|
842
|
+
* ```ts
|
|
843
|
+
* const numberValidator = new Validator((value) => {
|
|
844
|
+
* if (typeof value !== 'number') {
|
|
845
|
+
* throw new ValidationError('Expected number')
|
|
846
|
+
* }
|
|
847
|
+
* return value
|
|
848
|
+
* })
|
|
849
|
+
*
|
|
850
|
+
* const result = numberValidator.validate(42) // Returns 42 as number
|
|
851
|
+
* ```
|
|
852
|
+
* @public
|
|
853
|
+
*/
|
|
357
854
|
export declare class Validator<T> implements Validatable<T> {
|
|
358
855
|
readonly validationFn: ValidatorFn<T>;
|
|
359
856
|
readonly validateUsingKnownGoodVersionFn?: undefined | ValidatorUsingKnownGoodVersionFn<T>;
|
|
857
|
+
/**
|
|
858
|
+
* Creates a new Validator instance.
|
|
859
|
+
*
|
|
860
|
+
* validationFn - Function that validates and returns a value of type T
|
|
861
|
+
* validateUsingKnownGoodVersionFn - Optional performance-optimized validation function
|
|
862
|
+
*/
|
|
360
863
|
constructor(validationFn: ValidatorFn<T>, validateUsingKnownGoodVersionFn?: undefined | ValidatorUsingKnownGoodVersionFn<T>);
|
|
361
864
|
/**
|
|
362
|
-
*
|
|
865
|
+
* Validates an unknown value and returns it with the correct type. The returned value is
|
|
363
866
|
* guaranteed to be referentially equal to the passed value.
|
|
867
|
+
*
|
|
868
|
+
* @param value - The unknown value to validate
|
|
869
|
+
* @returns The validated value with type T
|
|
870
|
+
* @throws ValidationError When validation fails
|
|
871
|
+
* @example
|
|
872
|
+
* ```ts
|
|
873
|
+
* import { T } from '@tldraw/validate'
|
|
874
|
+
*
|
|
875
|
+
* const name = T.string.validate("Alice") // Returns "Alice" as string
|
|
876
|
+
* const title = T.string.validate("") // Returns "" (empty strings are valid)
|
|
877
|
+
*
|
|
878
|
+
* // These will throw ValidationError:
|
|
879
|
+
* T.string.validate(123) // Expected string, got a number
|
|
880
|
+
* T.string.validate(null) // Expected string, got null
|
|
881
|
+
* T.string.validate(undefined) // Expected string, got undefined
|
|
882
|
+
* ```
|
|
364
883
|
*/
|
|
365
884
|
validate(value: unknown): T;
|
|
885
|
+
/**
|
|
886
|
+
* Performance-optimized validation using a previously validated value. If the new value
|
|
887
|
+
* is referentially equal to the known good value, returns the known good value immediately.
|
|
888
|
+
*
|
|
889
|
+
* @param knownGoodValue - A previously validated value
|
|
890
|
+
* @param newValue - The new value to validate
|
|
891
|
+
* @returns The validated value, potentially reusing the known good value
|
|
892
|
+
* @throws ValidationError When validation fails
|
|
893
|
+
* @example
|
|
894
|
+
* ```ts
|
|
895
|
+
* import { T } from '@tldraw/validate'
|
|
896
|
+
*
|
|
897
|
+
* const userValidator = T.object({
|
|
898
|
+
* name: T.string,
|
|
899
|
+
* settings: T.object({ theme: T.literalEnum('light', 'dark') })
|
|
900
|
+
* })
|
|
901
|
+
*
|
|
902
|
+
* const user = userValidator.validate({ name: "Alice", settings: { theme: "light" } })
|
|
903
|
+
*
|
|
904
|
+
* // Later, with partially changed data:
|
|
905
|
+
* const newData = { name: "Alice", settings: { theme: "dark" } }
|
|
906
|
+
* const updated = userValidator.validateUsingKnownGoodVersion(user, newData)
|
|
907
|
+
* // Only validates the changed 'theme' field for better performance
|
|
908
|
+
* ```
|
|
909
|
+
*/
|
|
366
910
|
validateUsingKnownGoodVersion(knownGoodValue: T, newValue: unknown): T;
|
|
367
|
-
/**
|
|
911
|
+
/**
|
|
912
|
+
* Type guard that checks if a value is valid without throwing an error.
|
|
913
|
+
*
|
|
914
|
+
* @param value - The value to check
|
|
915
|
+
* @returns True if the value is valid, false otherwise
|
|
916
|
+
* @example
|
|
917
|
+
* ```ts
|
|
918
|
+
* import { T } from '@tldraw/validate'
|
|
919
|
+
*
|
|
920
|
+
* function processUserInput(input: unknown) {
|
|
921
|
+
* if (T.string.isValid(input)) {
|
|
922
|
+
* // input is now typed as string within this block
|
|
923
|
+
* return input.toUpperCase()
|
|
924
|
+
* }
|
|
925
|
+
* if (T.number.isValid(input)) {
|
|
926
|
+
* // input is now typed as number within this block
|
|
927
|
+
* return input.toFixed(2)
|
|
928
|
+
* }
|
|
929
|
+
* throw new Error('Expected string or number')
|
|
930
|
+
* }
|
|
931
|
+
* ```
|
|
932
|
+
*/
|
|
368
933
|
isValid(value: unknown): value is T;
|
|
369
934
|
/**
|
|
370
|
-
* Returns a new validator that also accepts null
|
|
371
|
-
*
|
|
935
|
+
* Returns a new validator that also accepts null values.
|
|
936
|
+
*
|
|
937
|
+
* @returns A new validator that accepts T or null
|
|
938
|
+
* @example
|
|
939
|
+
* ```ts
|
|
940
|
+
* import { T } from '@tldraw/validate'
|
|
941
|
+
*
|
|
942
|
+
* const assetValidator = T.object({
|
|
943
|
+
* id: T.string,
|
|
944
|
+
* name: T.string,
|
|
945
|
+
* src: T.srcUrl.nullable(), // Can be null if not loaded yet
|
|
946
|
+
* mimeType: T.string.nullable()
|
|
947
|
+
* })
|
|
948
|
+
*
|
|
949
|
+
* const asset = assetValidator.validate({
|
|
950
|
+
* id: "image-123",
|
|
951
|
+
* name: "photo.jpg",
|
|
952
|
+
* src: null, // Valid - asset not loaded yet
|
|
953
|
+
* mimeType: "image/jpeg"
|
|
954
|
+
* })
|
|
955
|
+
* ```
|
|
372
956
|
*/
|
|
373
957
|
nullable(): Validator<null | T>;
|
|
374
958
|
/**
|
|
375
|
-
* Returns a new validator that also accepts
|
|
376
|
-
*
|
|
959
|
+
* Returns a new validator that also accepts undefined values.
|
|
960
|
+
*
|
|
961
|
+
* @returns A new validator that accepts T or undefined
|
|
962
|
+
* @example
|
|
963
|
+
* ```ts
|
|
964
|
+
* import { T } from '@tldraw/validate'
|
|
965
|
+
*
|
|
966
|
+
* const shapeConfigValidator = T.object({
|
|
967
|
+
* type: T.literal('rectangle'),
|
|
968
|
+
* x: T.number,
|
|
969
|
+
* y: T.number,
|
|
970
|
+
* label: T.string.optional(), // Optional property
|
|
971
|
+
* metadata: T.object({ created: T.string }).optional()
|
|
972
|
+
* })
|
|
973
|
+
*
|
|
974
|
+
* // Both of these are valid:
|
|
975
|
+
* const shape1 = shapeConfigValidator.validate({ type: 'rectangle', x: 0, y: 0 })
|
|
976
|
+
* const shape2 = shapeConfigValidator.validate({
|
|
977
|
+
* type: 'rectangle', x: 0, y: 0, label: "My Shape"
|
|
978
|
+
* })
|
|
979
|
+
* ```
|
|
377
980
|
*/
|
|
378
981
|
optional(): Validator<T | undefined>;
|
|
379
982
|
/**
|
|
380
|
-
*
|
|
381
|
-
*
|
|
983
|
+
* Creates a new validator by refining this validator with additional logic that can transform
|
|
984
|
+
* the validated value to a new type.
|
|
985
|
+
*
|
|
986
|
+
* @param otherValidationFn - Function that transforms/validates the value to type U
|
|
987
|
+
* @returns A new validator that validates to type U
|
|
988
|
+
* @throws ValidationError When validation or refinement fails
|
|
989
|
+
* @example
|
|
990
|
+
* ```ts
|
|
991
|
+
* import { T, ValidationError } from '@tldraw/validate'
|
|
992
|
+
*
|
|
993
|
+
* // Transform string to ensure it starts with a prefix
|
|
994
|
+
* const prefixedIdValidator = T.string.refine((id) => {
|
|
995
|
+
* return id.startsWith('shape:') ? id : `shape:${id}`
|
|
996
|
+
* })
|
|
997
|
+
*
|
|
998
|
+
* const id1 = prefixedIdValidator.validate("rectangle-123") // Returns "shape:rectangle-123"
|
|
999
|
+
* const id2 = prefixedIdValidator.validate("shape:circle-456") // Returns "shape:circle-456"
|
|
1000
|
+
*
|
|
1001
|
+
* // Parse and validate JSON strings
|
|
1002
|
+
* const jsonValidator = T.string.refine((str) => {
|
|
1003
|
+
* try {
|
|
1004
|
+
* return JSON.parse(str)
|
|
1005
|
+
* } catch {
|
|
1006
|
+
* throw new ValidationError('Invalid JSON string')
|
|
1007
|
+
* }
|
|
1008
|
+
* })
|
|
1009
|
+
* ```
|
|
382
1010
|
*/
|
|
383
1011
|
refine<U>(otherValidationFn: (value: T) => U): Validator<U>;
|
|
384
1012
|
/**
|
|
385
|
-
*
|
|
1013
|
+
* Adds an additional validation check without changing the resulting value type.
|
|
1014
|
+
* Can be called with just a check function, or with a name for better error messages.
|
|
386
1015
|
*
|
|
1016
|
+
* @param name - Name for the check (used in error messages)
|
|
1017
|
+
* @param checkFn - Function that validates the value (should throw on invalid input)
|
|
1018
|
+
* @returns A new validator with the additional check
|
|
1019
|
+
* @throws ValidationError When the check fails
|
|
387
1020
|
* @example
|
|
388
|
-
*
|
|
389
1021
|
* ```ts
|
|
390
|
-
*
|
|
391
|
-
*
|
|
392
|
-
*
|
|
393
|
-
*
|
|
1022
|
+
* import { T, ValidationError } from '@tldraw/validate'
|
|
1023
|
+
*
|
|
1024
|
+
* // Basic check without name
|
|
1025
|
+
* const evenNumber = T.number.check((value) => {
|
|
1026
|
+
* if (value % 2 !== 0) {
|
|
1027
|
+
* throw new ValidationError('Expected even number')
|
|
1028
|
+
* }
|
|
1029
|
+
* })
|
|
1030
|
+
*
|
|
1031
|
+
* // Named checks for better error messages in complex validators
|
|
1032
|
+
* const shapePositionValidator = T.object({
|
|
1033
|
+
* x: T.number.check('finite', (value) => {
|
|
1034
|
+
* if (!Number.isFinite(value)) {
|
|
1035
|
+
* throw new ValidationError('Position must be finite')
|
|
1036
|
+
* }
|
|
1037
|
+
* }),
|
|
1038
|
+
* y: T.number.check('within-bounds', (value) => {
|
|
1039
|
+
* if (value < -10000 || value > 10000) {
|
|
1040
|
+
* throw new ValidationError('Position must be within bounds (-10000 to 10000)')
|
|
1041
|
+
* }
|
|
1042
|
+
* })
|
|
394
1043
|
* })
|
|
1044
|
+
*
|
|
1045
|
+
* // Error will be: "At x (check finite): Position must be finite"
|
|
395
1046
|
* ```
|
|
396
1047
|
*/
|
|
397
1048
|
check(name: string, checkFn: (value: T) => void): Validator<T>;
|
|
1049
|
+
/**
|
|
1050
|
+
* Adds an additional validation check without changing the resulting value type.
|
|
1051
|
+
*
|
|
1052
|
+
* @param checkFn - Function that validates the value (should throw on invalid input)
|
|
1053
|
+
* @returns A new validator with the additional check
|
|
1054
|
+
* @throws ValidationError When the check fails
|
|
1055
|
+
*/
|
|
398
1056
|
check(checkFn: (value: T) => void): Validator<T>;
|
|
399
1057
|
}
|
|
400
1058
|
|
|
401
|
-
/**
|
|
1059
|
+
/**
|
|
1060
|
+
* A function that validates and returns a value of type T from unknown input.
|
|
1061
|
+
* The function should throw a ValidationError if the value is invalid.
|
|
1062
|
+
*
|
|
1063
|
+
* @param value - The unknown value to validate
|
|
1064
|
+
* @returns The validated value of type T
|
|
1065
|
+
* @throws \{ValidationError\} When the value doesn't match the expected type
|
|
1066
|
+
* @example
|
|
1067
|
+
* ```ts
|
|
1068
|
+
* const stringValidator: ValidatorFn<string> = (value) => {
|
|
1069
|
+
* if (typeof value !== 'string') {
|
|
1070
|
+
* throw new ValidationError('Expected string')
|
|
1071
|
+
* }
|
|
1072
|
+
* return value
|
|
1073
|
+
* }
|
|
1074
|
+
* ```
|
|
1075
|
+
* @public
|
|
1076
|
+
*/
|
|
402
1077
|
declare type ValidatorFn<T> = (value: unknown) => T;
|
|
403
1078
|
|
|
404
|
-
/**
|
|
1079
|
+
/**
|
|
1080
|
+
* A performance-optimized validation function that can use a previously validated value
|
|
1081
|
+
* to avoid revalidating unchanged parts of the data structure.
|
|
1082
|
+
*
|
|
1083
|
+
* @param knownGoodValue - A previously validated value of type In
|
|
1084
|
+
* @param value - The unknown value to validate
|
|
1085
|
+
* @returns The validated value of type Out
|
|
1086
|
+
* @throws ValidationError When the value doesn't match the expected type
|
|
1087
|
+
* @example
|
|
1088
|
+
* ```ts
|
|
1089
|
+
* const optimizedValidator: ValidatorUsingKnownGoodVersionFn<User> = (
|
|
1090
|
+
* knownGood,
|
|
1091
|
+
* newValue
|
|
1092
|
+
* ) => {
|
|
1093
|
+
* if (Object.is(knownGood, newValue)) return knownGood
|
|
1094
|
+
* return fullValidation(newValue)
|
|
1095
|
+
* }
|
|
1096
|
+
* ```
|
|
1097
|
+
* @public
|
|
1098
|
+
*/
|
|
405
1099
|
declare type ValidatorUsingKnownGoodVersionFn<In, Out = In> = (knownGoodValue: In, value: unknown) => Out;
|
|
406
1100
|
|
|
407
1101
|
export { }
|