@tldraw/validate 5.1.0 → 5.2.0-canary.05c017c18b15

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.
@@ -40,7 +40,7 @@ class ValidationError extends Error {
40
40
  constructor(rawMessage, path = []) {
41
41
  const formattedPath = formatPath(path);
42
42
  const indentedMessage = rawMessage.split("\n").map((line, i) => i === 0 ? line : ` ${line}`).join("\n");
43
- super(path ? `At ${formattedPath}: ${indentedMessage}` : indentedMessage);
43
+ super(formattedPath ? `At ${formattedPath}: ${indentedMessage}` : indentedMessage);
44
44
  this.rawMessage = rawMessage;
45
45
  this.path = path;
46
46
  }
@@ -48,15 +48,11 @@ class ValidationError extends Error {
48
48
  path;
49
49
  name = "ValidationError";
50
50
  }
51
- function prefixError(path, fn) {
52
- try {
53
- return fn();
54
- } catch (err) {
55
- if (err instanceof ValidationError) {
56
- throw new ValidationError(err.rawMessage, [path, ...err.path]);
57
- }
58
- throw new ValidationError(err.toString(), [path]);
51
+ function rethrowPrefixed(path, err) {
52
+ if (err instanceof ValidationError) {
53
+ throw new ValidationError(err.rawMessage, [path, ...err.path]);
59
54
  }
55
+ throw new ValidationError(err.toString(), [path]);
60
56
  }
61
57
  function typeToString(value) {
62
58
  if (value === null) return "null";
@@ -285,7 +281,11 @@ class Validator {
285
281
  check(nameOrCheckFn, checkFn) {
286
282
  if (typeof nameOrCheckFn === "string") {
287
283
  return this.refine((value) => {
288
- prefixError(`(check ${nameOrCheckFn})`, () => checkFn(value));
284
+ try {
285
+ checkFn(value);
286
+ } catch (err) {
287
+ rethrowPrefixed(`(check ${nameOrCheckFn})`, err);
288
+ }
289
289
  return value;
290
290
  });
291
291
  } else {
@@ -307,17 +307,10 @@ class ArrayOfValidator extends Validator {
307
307
  (value) => {
308
308
  const arr = array.validate(value);
309
309
  for (let i = 0; i < arr.length; i++) {
310
- if (IS_DEV) {
311
- prefixError(i, () => itemValidator.validate(arr[i]));
312
- } else {
313
- try {
314
- itemValidator.validate(arr[i]);
315
- } catch (err) {
316
- if (err instanceof ValidationError) {
317
- throw new ValidationError(err.rawMessage, [i, ...err.path]);
318
- }
319
- throw new ValidationError(err.toString(), [i]);
320
- }
310
+ try {
311
+ itemValidator.validate(arr[i]);
312
+ } catch (err) {
313
+ rethrowPrefixed(i, err);
321
314
  }
322
315
  }
323
316
  return arr;
@@ -333,46 +326,26 @@ class ArrayOfValidator extends Validator {
333
326
  const item = arr[i];
334
327
  if (i >= knownGoodValue.length) {
335
328
  isDifferent = true;
336
- if (IS_DEV) {
337
- prefixError(i, () => itemValidator.validate(item));
338
- } else {
339
- try {
340
- itemValidator.validate(item);
341
- } catch (err) {
342
- if (err instanceof ValidationError) {
343
- throw new ValidationError(err.rawMessage, [i, ...err.path]);
344
- }
345
- throw new ValidationError(err.toString(), [i]);
346
- }
329
+ try {
330
+ itemValidator.validate(item);
331
+ } catch (err) {
332
+ rethrowPrefixed(i, err);
347
333
  }
348
334
  continue;
349
335
  }
350
336
  if (Object.is(knownGoodValue[i], item)) {
351
337
  continue;
352
338
  }
353
- if (IS_DEV) {
354
- const checkedItem = prefixError(
355
- i,
356
- () => itemValidator.validateUsingKnownGoodVersion(knownGoodValue[i], item)
339
+ try {
340
+ const checkedItem = itemValidator.validateUsingKnownGoodVersion(
341
+ knownGoodValue[i],
342
+ item
357
343
  );
358
344
  if (!Object.is(checkedItem, knownGoodValue[i])) {
359
345
  isDifferent = true;
360
346
  }
361
- } else {
362
- try {
363
- const checkedItem = itemValidator.validateUsingKnownGoodVersion(
364
- knownGoodValue[i],
365
- item
366
- );
367
- if (!Object.is(checkedItem, knownGoodValue[i])) {
368
- isDifferent = true;
369
- }
370
- } catch (err) {
371
- if (err instanceof ValidationError) {
372
- throw new ValidationError(err.rawMessage, [i, ...err.path]);
373
- }
374
- throw new ValidationError(err.toString(), [i]);
375
- }
347
+ } catch (err) {
348
+ rethrowPrefixed(i, err);
376
349
  }
377
350
  }
378
351
  return isDifferent ? newValue : knownGoodValue;
@@ -428,33 +401,28 @@ class ObjectValidator extends Validator {
428
401
  * shouldAllowUnknownProperties - Whether to allow properties not defined in config
429
402
  */
430
403
  constructor(config, shouldAllowUnknownProperties = false) {
404
+ const configKeys = [];
405
+ const configValidators = [];
406
+ for (const [key, validator] of Object.entries(config)) {
407
+ configKeys.push(key);
408
+ configValidators.push(validator);
409
+ }
431
410
  super(
432
411
  (object2) => {
433
412
  if (typeof object2 !== "object" || object2 === null) {
434
413
  throw new ValidationError(`Expected object, got ${typeToString(object2)}`);
435
414
  }
436
- for (const key in config) {
437
- if (!hasOwnProperty(config, key)) continue;
438
- const validator = config[key];
439
- if (IS_DEV) {
440
- prefixError(key, () => {
441
- ;
442
- validator.validate(getOwnProperty(object2, key));
443
- });
444
- } else {
445
- try {
446
- ;
447
- validator.validate(getOwnProperty(object2, key));
448
- } catch (err) {
449
- if (err instanceof ValidationError) {
450
- throw new ValidationError(err.rawMessage, [key, ...err.path]);
451
- }
452
- throw new ValidationError(err.toString(), [key]);
453
- }
415
+ for (let i = 0; i < configKeys.length; i++) {
416
+ const key = configKeys[i];
417
+ try {
418
+ configValidators[i].validate(getOwnProperty(object2, key));
419
+ } catch (err) {
420
+ rethrowPrefixed(key, err);
454
421
  }
455
422
  }
456
423
  if (!shouldAllowUnknownProperties) {
457
- for (const key of Object.keys(object2)) {
424
+ for (const key in object2) {
425
+ if (!hasOwnProperty(object2, key)) continue;
458
426
  if (!hasOwnProperty(config, key)) {
459
427
  throw new ValidationError(`Unexpected property`, [key]);
460
428
  }
@@ -470,52 +438,45 @@ class ObjectValidator extends Validator {
470
438
  throw new ValidationError(`Expected object, got ${typeToString(newValue)}`);
471
439
  }
472
440
  let isDifferent = false;
473
- for (const key in config) {
474
- if (!hasOwnProperty(config, key)) continue;
475
- const validator = config[key];
441
+ for (let i = 0; i < configKeys.length; i++) {
442
+ const key = configKeys[i];
476
443
  const prev = getOwnProperty(knownGoodValue, key);
477
444
  const next = getOwnProperty(newValue, key);
478
445
  if (Object.is(prev, next)) {
479
446
  continue;
480
447
  }
481
- if (IS_DEV) {
482
- const checked = prefixError(key, () => {
483
- const validatable = validator;
484
- if (validatable.validateUsingKnownGoodVersion) {
485
- return validatable.validateUsingKnownGoodVersion(prev, next);
486
- } else {
487
- return validatable.validate(next);
488
- }
489
- });
448
+ try {
449
+ const validator = configValidators[i];
450
+ const checked = validator.validateUsingKnownGoodVersion ? validator.validateUsingKnownGoodVersion(prev, next) : validator.validate(next);
490
451
  if (!Object.is(checked, prev)) {
491
452
  isDifferent = true;
492
453
  }
493
- } else {
494
- try {
495
- const validatable = validator;
496
- const checked = validatable.validateUsingKnownGoodVersion ? validatable.validateUsingKnownGoodVersion(prev, next) : validatable.validate(next);
497
- if (!Object.is(checked, prev)) {
498
- isDifferent = true;
499
- }
500
- } catch (err) {
501
- if (err instanceof ValidationError) {
502
- throw new ValidationError(err.rawMessage, [key, ...err.path]);
503
- }
504
- throw new ValidationError(err.toString(), [key]);
505
- }
454
+ } catch (err) {
455
+ rethrowPrefixed(key, err);
506
456
  }
507
457
  }
508
458
  if (!shouldAllowUnknownProperties) {
509
- for (const key of Object.keys(newValue)) {
459
+ for (const key in newValue) {
460
+ if (!hasOwnProperty(newValue, key)) continue;
510
461
  if (!hasOwnProperty(config, key)) {
511
462
  throw new ValidationError(`Unexpected property`, [key]);
512
463
  }
513
464
  }
465
+ } else if (!isDifferent) {
466
+ for (const key of Object.keys(newValue)) {
467
+ if (!hasOwnProperty(config, key) && !Object.is(getOwnProperty(knownGoodValue, key), getOwnProperty(newValue, key))) {
468
+ isDifferent = true;
469
+ break;
470
+ }
471
+ }
514
472
  }
515
- for (const key of Object.keys(knownGoodValue)) {
516
- if (!hasOwnProperty(newValue, key)) {
517
- isDifferent = true;
518
- break;
473
+ if (!isDifferent) {
474
+ for (const key in knownGoodValue) {
475
+ if (!hasOwnProperty(knownGoodValue, key)) continue;
476
+ if (!hasOwnProperty(newValue, key)) {
477
+ isDifferent = true;
478
+ break;
479
+ }
519
480
  }
520
481
  }
521
482
  return isDifferent ? newValue : knownGoodValue;
@@ -571,29 +532,35 @@ class UnionValidator extends Validator {
571
532
  super(
572
533
  (input) => {
573
534
  this.expectObject(input);
574
- const { matchingSchema, variant } = this.getMatchingSchemaAndVariant(input);
535
+ const matchingSchema = this.getMatchingSchema(input);
575
536
  if (matchingSchema === void 0) {
576
- return this.unknownValueValidation(input, variant);
537
+ return this.unknownValueValidation(input, this.getVariant(input));
538
+ }
539
+ try {
540
+ return matchingSchema.validate(input);
541
+ } catch (err) {
542
+ rethrowPrefixed(`(${key} = ${this.getVariant(input)})`, err);
577
543
  }
578
- return prefixError(`(${key} = ${variant})`, () => matchingSchema.validate(input));
579
544
  },
580
545
  (prevValue, newValue) => {
581
546
  this.expectObject(newValue);
582
547
  this.expectObject(prevValue);
583
- const { matchingSchema, variant } = this.getMatchingSchemaAndVariant(newValue);
548
+ const matchingSchema = this.getMatchingSchema(newValue);
584
549
  if (matchingSchema === void 0) {
585
- return this.unknownValueValidation(newValue, variant);
586
- }
587
- if (getOwnProperty(prevValue, key) !== getOwnProperty(newValue, key)) {
588
- return prefixError(`(${key} = ${variant})`, () => matchingSchema.validate(newValue));
550
+ return this.unknownValueValidation(newValue, this.getVariant(newValue));
589
551
  }
590
- return prefixError(`(${key} = ${variant})`, () => {
552
+ try {
553
+ if (getOwnProperty(prevValue, key) !== getOwnProperty(newValue, key)) {
554
+ return matchingSchema.validate(newValue);
555
+ }
591
556
  if (matchingSchema.validateUsingKnownGoodVersion) {
592
557
  return matchingSchema.validateUsingKnownGoodVersion(prevValue, newValue);
593
558
  } else {
594
559
  return matchingSchema.validate(newValue);
595
560
  }
596
- });
561
+ } catch (err) {
562
+ rethrowPrefixed(`(${key} = ${this.getVariant(newValue)})`, err);
563
+ }
597
564
  }
598
565
  );
599
566
  this.key = key;
@@ -610,7 +577,13 @@ class UnionValidator extends Validator {
610
577
  throw new ValidationError(`Expected an object, got ${typeToString(value)}`, []);
611
578
  }
612
579
  }
613
- getMatchingSchemaAndVariant(object2) {
580
+ getVariant(object2) {
581
+ return getOwnProperty(object2, this.key);
582
+ }
583
+ // Returns the matching schema for the object's variant, or undefined if the variant is
584
+ // unknown. The variant itself is only needed on cold paths (unknown variants and errors),
585
+ // so this avoids allocating a `{ matchingSchema, variant }` result per validation.
586
+ getMatchingSchema(object2) {
614
587
  const variant = getOwnProperty(object2, this.key);
615
588
  if (!this.useNumberKeys && typeof variant !== "string") {
616
589
  throw new ValidationError(
@@ -624,8 +597,7 @@ class UnionValidator extends Validator {
624
597
  );
625
598
  }
626
599
  }
627
- const matchingSchema = hasOwnProperty(this.config, variant) ? this.config[variant] : void 0;
628
- return { matchingSchema, variant };
600
+ return hasOwnProperty(this.config, variant) ? this.config[variant] : void 0;
629
601
  }
630
602
  /**
631
603
  * Returns a new UnionValidator that can handle unknown variants using the provided function.
@@ -660,21 +632,11 @@ class DictValidator extends Validator {
660
632
  }
661
633
  for (const key in object2) {
662
634
  if (!hasOwnProperty(object2, key)) continue;
663
- if (IS_DEV) {
664
- prefixError(key, () => {
665
- keyValidator.validate(key);
666
- valueValidator.validate(object2[key]);
667
- });
668
- } else {
669
- try {
670
- keyValidator.validate(key);
671
- valueValidator.validate(object2[key]);
672
- } catch (err) {
673
- if (err instanceof ValidationError) {
674
- throw new ValidationError(err.rawMessage, [key, ...err.path]);
675
- }
676
- throw new ValidationError(err.toString(), [key]);
677
- }
635
+ try {
636
+ keyValidator.validate(key);
637
+ valueValidator.validate(object2[key]);
638
+ } catch (err) {
639
+ rethrowPrefixed(key, err);
678
640
  }
679
641
  }
680
642
  return object2;
@@ -692,21 +654,11 @@ class DictValidator extends Validator {
692
654
  const next = newObj[key];
693
655
  if (!hasOwnProperty(knownGoodValue, key)) {
694
656
  isDifferent = true;
695
- if (IS_DEV) {
696
- prefixError(key, () => {
697
- keyValidator.validate(key);
698
- valueValidator.validate(next);
699
- });
700
- } else {
701
- try {
702
- keyValidator.validate(key);
703
- valueValidator.validate(next);
704
- } catch (err) {
705
- if (err instanceof ValidationError) {
706
- throw new ValidationError(err.rawMessage, [key, ...err.path]);
707
- }
708
- throw new ValidationError(err.toString(), [key]);
709
- }
657
+ try {
658
+ keyValidator.validate(key);
659
+ valueValidator.validate(next);
660
+ } catch (err) {
661
+ rethrowPrefixed(key, err);
710
662
  }
711
663
  continue;
712
664
  }
@@ -714,29 +666,13 @@ class DictValidator extends Validator {
714
666
  if (Object.is(prev, next)) {
715
667
  continue;
716
668
  }
717
- if (IS_DEV) {
718
- const checked = prefixError(key, () => {
719
- if (valueValidator.validateUsingKnownGoodVersion) {
720
- return valueValidator.validateUsingKnownGoodVersion(prev, next);
721
- } else {
722
- return valueValidator.validate(next);
723
- }
724
- });
669
+ try {
670
+ const checked = valueValidator.validateUsingKnownGoodVersion ? valueValidator.validateUsingKnownGoodVersion(prev, next) : valueValidator.validate(next);
725
671
  if (!Object.is(checked, prev)) {
726
672
  isDifferent = true;
727
673
  }
728
- } else {
729
- try {
730
- const checked = valueValidator.validateUsingKnownGoodVersion ? valueValidator.validateUsingKnownGoodVersion(prev, next) : valueValidator.validate(next);
731
- if (!Object.is(checked, prev)) {
732
- isDifferent = true;
733
- }
734
- } catch (err) {
735
- if (err instanceof ValidationError) {
736
- throw new ValidationError(err.rawMessage, [key, ...err.path]);
737
- }
738
- throw new ValidationError(err.toString(), [key]);
739
- }
674
+ } catch (err) {
675
+ rethrowPrefixed(key, err);
740
676
  }
741
677
  }
742
678
  if (!isDifferent) {
@@ -926,10 +862,17 @@ function isValidJson(value) {
926
862
  return true;
927
863
  }
928
864
  if (Array.isArray(value)) {
929
- return value.every(isValidJson);
865
+ for (let i = 0; i < value.length; i++) {
866
+ if (!isValidJson(value[i])) return false;
867
+ }
868
+ return true;
930
869
  }
931
870
  if (isPlainObject(value)) {
932
- return Object.values(value).every(isValidJson);
871
+ for (const key in value) {
872
+ if (!hasOwnProperty(value, key)) continue;
873
+ if (!isValidJson(value[key])) return false;
874
+ }
875
+ return true;
933
876
  }
934
877
  return false;
935
878
  }
@@ -962,7 +905,8 @@ const jsonValue = new Validator(
962
905
  return isDifferent ? newValue : knownGoodValue;
963
906
  } else if (isPlainObject(knownGoodValue) && isPlainObject(newValue)) {
964
907
  let isDifferent = false;
965
- for (const key of Object.keys(newValue)) {
908
+ for (const key in newValue) {
909
+ if (!hasOwnProperty(newValue, key)) continue;
966
910
  if (!hasOwnProperty(knownGoodValue, key)) {
967
911
  isDifferent = true;
968
912
  jsonValue.validate(newValue[key]);
@@ -978,10 +922,13 @@ const jsonValue = new Validator(
978
922
  isDifferent = true;
979
923
  }
980
924
  }
981
- for (const key of Object.keys(knownGoodValue)) {
982
- if (!hasOwnProperty(newValue, key)) {
983
- isDifferent = true;
984
- break;
925
+ if (!isDifferent) {
926
+ for (const key in knownGoodValue) {
927
+ if (!hasOwnProperty(knownGoodValue, key)) continue;
928
+ if (!hasOwnProperty(newValue, key)) {
929
+ isDifferent = true;
930
+ break;
931
+ }
985
932
  }
986
933
  }
987
934
  return isDifferent ? newValue : knownGoodValue;
@@ -1025,16 +972,22 @@ function numberUnion(key, config) {
1025
972
  function model(name, validator) {
1026
973
  return new Validator(
1027
974
  (value) => {
1028
- return prefixError(name, () => validator.validate(value));
975
+ try {
976
+ return validator.validate(value);
977
+ } catch (err) {
978
+ rethrowPrefixed(name, err);
979
+ }
1029
980
  },
1030
981
  (prevValue, newValue) => {
1031
- return prefixError(name, () => {
982
+ try {
1032
983
  if (validator.validateUsingKnownGoodVersion) {
1033
984
  return validator.validateUsingKnownGoodVersion(prevValue, newValue);
1034
985
  } else {
1035
986
  return validator.validate(newValue);
1036
987
  }
1037
- });
988
+ } catch (err) {
989
+ rethrowPrefixed(name, err);
990
+ }
1038
991
  }
1039
992
  );
1040
993
  }