4a2040f92c
This PR adds a validation mode whereby previous known-to-be-valid values can be used to speed up the validation process itself. At the same time it enables us to do fine-grained equality checking on records much more quickly than by using something like lodash isEqual, and using that we can prevent triggering effects for record updates that don't actually alter any values in the store. Here's some preliminary perf testing of average time spent in `store.put()` during some common interactions | task | before (ms) | after (ms) | | ---- | ---- | ---- | | drawing lines | 0.0403 | 0.0214 | | drawing boxes | 0.0408 | 0.0348 | | translating lines | 0.0352 | 0.0042 | | translating boxes | 0.0051 | 0.0032 | | rotating lines | 0.0312 | 0.0065 | | rotating boxes | 0.0053 | 0.0035 | | brush selecting boxes | 0.0200 | 0.0232 | | traversal with shapes | 0.0130 | 0.0108 | | traversal without shapes | 0.0201 | 0.0173 | **traversal** means moving the camera and pointer around the canvas #### Discussion At the scale of hundredths of a millisecond these .put operations are so fast that even if they became literally instantaneous the change would not be human perceptible. That said, there is an overall marked improvement here. Especially for dealing with draw shapes. These figures are also mostly in line with expectations, aside from a couple of things: - I don't understand why the `brush selecting boxes` task got slower after the change. - I don't understand why the `traversal` tasks are slower than the `translating boxes` task, both before and after. I would expect that .putting shape records would be much slower than .putting pointer/camera records (since the latter have fewer and simpler properties) ### Change Type - [x] `patch` — Bug fix ### Test Plan 1. Add a step-by-step description of how to test your PR here. 2. - [ ] Unit Tests - [ ] End to end tests ### Release Notes - Add a brief release note for your PR here.
6.6 KiB
6.6 KiB
API Report File for "@tldraw/validate"
Do not edit this file. It is a report generated by API Extractor.
import { IndexKey } from '@tldraw/utils';
import { JsonValue } from '@tldraw/utils';
// @public
const any: Validator<any>;
// @public
const array: Validator<unknown[]>;
// @public
function arrayOf<T>(itemValidator: Validatable<T>): ArrayOfValidator<T>;
// @public (undocumented)
export class ArrayOfValidator<T> extends Validator<T[]> {
constructor(itemValidator: Validatable<T>);
// (undocumented)
readonly itemValidator: Validatable<T>;
// (undocumented)
lengthGreaterThan1(): Validator<T[]>;
// (undocumented)
nonEmpty(): Validator<T[]>;
}
// @public
const bigint: Validator<bigint>;
// @public
const boolean: Validator<boolean>;
// @public
function dict<Key extends string, Value>(keyValidator: Validatable<Key>, valueValidator: Validatable<Value>): DictValidator<Key, Value>;
// @public (undocumented)
export class DictValidator<Key extends string, Value> extends Validator<Record<Key, Value>> {
constructor(keyValidator: Validatable<Key>, valueValidator: Validatable<Value>);
// (undocumented)
readonly keyValidator: Validatable<Key>;
// (undocumented)
readonly valueValidator: Validatable<Value>;
}
// @public
const indexKey: Validator<IndexKey>;
// @public
const integer: Validator<number>;
// @public
function jsonDict(): DictValidator<string, JsonValue>;
// @public
const jsonValue: Validator<JsonValue>;
// @public
const linkUrl: Validator<string>;
// @public
function literal<T extends boolean | number | string>(expectedValue: T): Validator<T>;
// @public (undocumented)
function literalEnum<const Values extends readonly unknown[]>(...values: Values): Validator<Values[number]>;
// @public
function model<T extends {
readonly id: string;
}>(name: string, validator: Validatable<T>): Validator<T>;
// @public
const nonZeroInteger: Validator<number>;
// @public
const nonZeroNumber: Validator<number>;
// @public (undocumented)
function nullable<T>(validator: Validatable<T>): Validator<null | T>;
// @public
const number: Validator<number>;
// @public
function object<Shape extends object>(config: {
readonly [K in keyof Shape]: Validatable<Shape[K]>;
}): ObjectValidator<{
[P in ExtractRequiredKeys<Shape>]: Shape[P];
} & {
[P in ExtractOptionalKeys<Shape>]?: Shape[P];
}>;
// @public (undocumented)
export class ObjectValidator<Shape extends object> extends Validator<Shape> {
constructor(config: {
readonly [K in keyof Shape]: Validatable<Shape[K]>;
}, shouldAllowUnknownProperties?: boolean);
// (undocumented)
allowUnknownProperties(): ObjectValidator<Shape>;
// (undocumented)
readonly config: {
readonly [K in keyof Shape]: Validatable<Shape[K]>;
};
extend<Extension extends Record<string, unknown>>(extension: {
readonly [K in keyof Extension]: Validatable<Extension[K]>;
}): ObjectValidator<Shape & Extension>;
}
// @public (undocumented)
function optional<T>(validator: Validatable<T>): Validator<T | undefined>;
// @public
const positiveInteger: Validator<number>;
// @public
const positiveNumber: Validator<number>;
// @public (undocumented)
function setEnum<T>(values: ReadonlySet<T>): Validator<T>;
// @public
const srcUrl: Validator<string>;
// @public
const string: Validator<string>;
declare namespace T {
export {
literal,
arrayOf,
object,
jsonDict,
dict,
union,
model,
setEnum,
optional,
nullable,
literalEnum,
ValidatorFn,
ValidatorUsingKnownGoodVersionFn,
Validatable,
ValidationError,
TypeOf,
Validator,
ArrayOfValidator,
ObjectValidator,
UnionValidator,
DictValidator,
unknown,
any,
string,
number,
positiveNumber,
nonZeroNumber,
integer,
positiveInteger,
nonZeroInteger,
boolean,
bigint,
array,
unknownObject,
jsonValue,
linkUrl,
srcUrl,
indexKey
}
}
export { T }
// @public (undocumented)
type TypeOf<V extends Validatable<any>> = V extends Validatable<infer T> ? T : never;
// @public
function union<Key extends string, Config extends UnionValidatorConfig<Key, Config>>(key: Key, config: Config): UnionValidator<Key, Config>;
// @public (undocumented)
export class UnionValidator<Key extends string, Config extends UnionValidatorConfig<Key, Config>, UnknownValue = never> extends Validator<TypeOf<Config[keyof Config]> | UnknownValue> {
constructor(key: Key, config: Config, unknownValueValidation: (value: object, variant: string) => UnknownValue);
// (undocumented)
validateUnknownVariants<Unknown>(unknownValueValidation: (value: object, variant: string) => Unknown): UnionValidator<Key, Config, Unknown>;
}
// @public
const unknown: Validator<unknown>;
// @public (undocumented)
const unknownObject: Validator<Record<string, unknown>>;
// @public (undocumented)
type Validatable<T> = {
validate: (value: unknown) => T;
validateUsingKnownGoodVersion?: (knownGoodValue: T, newValue: unknown) => T;
};
// @public (undocumented)
class ValidationError extends Error {
constructor(rawMessage: string, path?: ReadonlyArray<number | string>);
// (undocumented)
name: string;
// (undocumented)
readonly path: ReadonlyArray<number | string>;
// (undocumented)
readonly rawMessage: string;
}
// @public (undocumented)
export class Validator<T> implements Validatable<T> {
constructor(validationFn: ValidatorFn<T>, validateUsingKnownGoodVersionFn?: undefined | ValidatorUsingKnownGoodVersionFn<T, T>);
check(name: string, checkFn: (value: T) => void): Validator<T>;
// (undocumented)
check(checkFn: (value: T) => void): Validator<T>;
isValid(value: unknown): value is T;
nullable(): Validator<null | T>;
optional(): Validator<T | undefined>;
refine<U>(otherValidationFn: (value: T) => U): Validator<U>;
validate(value: unknown): T;
// (undocumented)
validateUsingKnownGoodVersion(knownGoodValue: T, newValue: unknown): T;
// (undocumented)
readonly validateUsingKnownGoodVersionFn?: undefined | ValidatorUsingKnownGoodVersionFn<T, T>;
// (undocumented)
readonly validationFn: ValidatorFn<T>;
}
// @public (undocumented)
type ValidatorFn<T> = (value: unknown) => T;
// @public (undocumented)
type ValidatorUsingKnownGoodVersionFn<In, Out = In> = (knownGoodValue: In, value: unknown) => Out;
// (No @packageDocumentation comment for this package)