@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.
@@ -3,124 +3,319 @@ import { JsonValue } from '@tldraw/utils';
3
3
  import { MakeUndefinedOptional } from '@tldraw/utils';
4
4
 
5
5
  /**
6
- * Validation that accepts any value. Generally this should be avoided, but you can use it as an
7
- * escape hatch if you want to work without validations for e.g. a prototype.
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
- * Validates that a value is an array. To check the contents of the array, use T.arrayOf.
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
- * Validates that a value is an array whose contents matches the passed-in validator.
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
- /** @public */
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
- * Validates that a value is a bigint.
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
- * Validates that a value is boolean.
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
- * Validation that an option is a dict with particular keys and values.
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
- /** @public */
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
- * Validates an http(s) url
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
- * Validates that a value is an IndexKey.
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
- * Fails if number is not an integer
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
- * Validate an object has a particular shape.
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
- * Validate that a value is valid JSON.
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
- * Validates that a value is a url safe to use as a link.
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
- * Validates that a value matches another that was passed in.
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
- /** @public */
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
- * A named object with an ID. Errors will be reported as being part of the object with the given
122
- * name.
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
- * Fails if value \<= 0 and is not an integer
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
- * Fails if value \<= 0
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
- /** @public */
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
- * Validates that a value is a finite non-NaN number.
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
- * Validate an object has a particular shape.
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
- /** @public */
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
- * Extend an object validator by adding additional properties.
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 animalValidator = T.object({
182
- * name: T.string,
183
- * })
184
- * const catValidator = animalValidator.extend({
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
- /** @public */
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
- * Validate a value against one of two types.
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
- * Fails if value \< 0 and is not an integer
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
- * Fails if value \< 0
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
- /** @public */
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
- * Validates that a valid is a url safe to load as an asset.
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
- * Validates that a value is a string.
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
- /** @public */
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
- * Validate a union of several object types. Each object must have a property matching `key` which
287
- * should be a unique string.
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 catValidator = T.object({ kind: T.literal('cat'), meow: T.boolean })
293
- * const dogValidator = T.object({ kind: T.literal('dog'), bark: T.boolean })
294
- * const animalValidator = T.union('kind', { cat: catValidator, dog: dogValidator })
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
- /** @public */
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
- /** @public */
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
- * Validation that accepts any value. Useful as a starting point for building your own custom
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
- /** @public */
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
- /** @public */
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
- * This is a performance optimizing version of validate that can use a previous
338
- * version of the value to avoid revalidating every part of the new value if
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
- * @returns
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
- /** @public */
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
- /** @public */
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
- * Asserts that the passed value is of the correct type and returns it. The returned value is
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
- /** Checks that the passed value is of the correct type. */
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 or undefined. The resulting value will always be
371
- * null.
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 null or undefined. The resulting value will always be
376
- * null.
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
- * Refine this validation to a new type. The passed-in validation function should throw an error
381
- * if the value can't be converted to the new type, or return the new type otherwise.
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
- * Refine this validation with an additional check that doesn't change the resulting value.
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
- * const numberLessThan10Validator = T.number.check((value) => {
391
- * if (value >= 10) {
392
- * throw new ValidationError(`Expected number less than 10, got ${value}`)
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
- /** @public */
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
- /** @public */
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 { }