tldraw/packages/tlstore/api-report.md
David Sheldrick a48a55d3de
[perf] make ensureStoreIsUsable scale better (#1362)
The new version of the sync engine is gonna be calling
`ensureStoreIsUsable` on every sync message, so I took some time to make
it scale better. At the moment it operates on a serialized version of
the store, which is expensive and unnecessary. Here I changed it to use
reactive queries for the data it needs, so it only operates on small
bits of data and should not become more expensive as the number of
shapes grows.

### Change Type

<!-- 💡 Indicate the type of change your pull request is. -->
<!-- 🤷‍♀️ If you're not sure, don't select anything -->
<!-- ✂️ Feel free to delete unselected options -->

<!-- To select one, put an x in the box: [x] -->

- [x] `patch` — Bug Fix
- [ ] `minor` — New Feature
- [ ] `major` — Breaking Change

- [ ] `dependencies` — Dependency Update (publishes a `patch` release,
for devDependencies use `internal`)

- [ ] `documentation` — Changes to the documentation only (will not
publish a new version)
- [ ] `tests` — Changes to any testing-related code only (will not
publish a new version)
- [ ] `internal` — Any other changes that don't affect the published
package (will not publish a new version)

### Test Plan

1. Add a step-by-step description of how to test your PR here.
2.

- [ ] Unit Tests
- [ ] Webdriver tests

### Release Notes

- Add a brief release note for your PR here.
2023-05-12 11:39:36 +00:00

10 KiB

API Report File for "@tldraw/tlstore"

Do not edit this file. It is a report generated by API Extractor.


import { Atom } from 'signia';
import { Computed } from 'signia';
import { Signal } from 'signia';

// @public
export type AllRecords<T extends Store<any>> = ExtractR<ExtractRecordType<T>>;

// @public
export function assertIdType<R extends BaseRecord>(id: string | undefined, type: RecordType<R, any>): asserts id is ID<R>;

// @public
export interface BaseRecord<TypeName extends string = string> {
    // (undocumented)
    readonly id: ID<this>;
    // (undocumented)
    readonly typeName: TypeName;
}

// @public
export type CollectionDiff<T> = {
    added?: Set<T>;
    removed?: Set<T>;
};

// @public (undocumented)
export function compareRecordVersions(a: RecordVersion, b: RecordVersion): -1 | 0 | 1;

// @public (undocumented)
export const compareSchemas: (a: SerializedSchema, b: SerializedSchema) => -1 | 0 | 1;

// @public
export type ComputedCache<Data, R extends BaseRecord> = {
    get(id: ID<R>): Data | undefined;
};

// @public
export function createRecordType<R extends BaseRecord>(typeName: R['typeName'], config: {
    migrations?: Migrations;
    validator: StoreValidator<R>;
    scope: Scope;
}): RecordType<R, keyof Omit<R, 'id' | 'typeName'>>;

// @public (undocumented)
export function defineMigrations<FirstVersion extends number, CurrentVersion extends number>({ firstVersion, currentVersion, migrators, subTypeKey, subTypeMigrations, }: {
    firstVersion: FirstVersion;
    currentVersion: CurrentVersion;
    migrators: {
        [version in Exclude<Range_2<FirstVersion, CurrentVersion>, FirstVersion>]: Migration;
    };
    subTypeKey?: string;
    subTypeMigrations?: Record<string, BaseMigrationsInfo>;
}): Migrations;

// @public
export function devFreeze<T>(object: T): T;

// @public (undocumented)
export function getRecordVersion(record: BaseRecord, serializedSchema: SerializedSchema): RecordVersion;

// @public
export type HistoryEntry<R extends BaseRecord = BaseRecord> = {
    changes: RecordsDiff<R>;
    source: 'remote' | 'user';
};

// @public (undocumented)
export type ID<R extends BaseRecord = BaseRecord> = string & {
    __type__: R;
};

// @internal
export class IncrementalSetConstructor<T> {
    constructor(
    previousValue: Set<T>);
    // @public
    add(item: T): void;
    // @public
    get(): {
        value: Set<T>;
        diff: CollectionDiff<T>;
    } | undefined;
    // @public
    remove(item: T): void;
}

// @public (undocumented)
export function migrate<T>({ value, migrations, fromVersion, toVersion, }: {
    value: unknown;
    migrations: Migrations;
    fromVersion: number;
    toVersion: number;
}): MigrationResult<T>;

// @public (undocumented)
export function migrateRecord<R extends BaseRecord>({ record, migrations, fromVersion, toVersion, }: {
    record: unknown;
    migrations: Migrations;
    fromVersion: number;
    toVersion: number;
}): MigrationResult<R>;

// @public (undocumented)
export type Migration<T = any> = {
    up: (oldState: T) => T;
    down: (newState: T) => T;
};

// @public (undocumented)
export enum MigrationFailureReason {
    // (undocumented)
    IncompatibleSubtype = "incompatible-subtype",
    // (undocumented)
    MigrationError = "migration-error",
    // (undocumented)
    TargetVersionTooNew = "target-version-too-new",
    // (undocumented)
    TargetVersionTooOld = "target-version-too-old",
    // (undocumented)
    UnknownType = "unknown-type",
    // (undocumented)
    UnrecognizedSubtype = "unrecognized-subtype"
}

// @public (undocumented)
export type MigrationResult<T> = {
    type: 'error';
    reason: MigrationFailureReason;
} | {
    type: 'success';
    value: T;
};

// @public (undocumented)
export interface Migrations extends BaseMigrationsInfo {
    // (undocumented)
    subTypeKey?: string;
    // (undocumented)
    subTypeMigrations?: Record<string, BaseMigrationsInfo>;
}

// @public
export type RecordsDiff<R extends BaseRecord> = {
    added: Record<string, R>;
    updated: Record<string, [from: R, to: R]>;
    removed: Record<string, R>;
};

// @public
export class RecordType<R extends BaseRecord, RequiredProperties extends keyof Omit<R, 'id' | 'typeName'>> {
    constructor(
    typeName: R['typeName'], config: {
        readonly createDefaultProperties: () => Exclude<OmitMeta<R>, RequiredProperties>;
        readonly migrations: Migrations;
        readonly validator?: {
            validate: (r: unknown) => R;
        } | StoreValidator<R>;
        readonly scope?: Scope;
    });
    clone(record: R): R;
    create(properties: Pick<R, RequiredProperties> & Omit<Partial<R>, RequiredProperties>): R;
    createCustomId(id: string): ID<R>;
    // (undocumented)
    readonly createDefaultProperties: () => Exclude<OmitMeta<R>, RequiredProperties>;
    createId(): ID<R>;
    isId(id?: string): id is ID<R>;
    isInstance: (record?: BaseRecord) => record is R;
    // (undocumented)
    readonly migrations: Migrations;
    parseId(id: string): ID<R>;
    // (undocumented)
    readonly scope: Scope;
    readonly typeName: R['typeName'];
    validate(record: unknown): R;
    // (undocumented)
    readonly validator: {
        validate: (r: unknown) => R;
    } | StoreValidator<R>;
    withDefaultProperties<DefaultProps extends Omit<Partial<R>, 'id' | 'typeName'>>(createDefaultProperties: () => DefaultProps): RecordType<R, Exclude<RequiredProperties, keyof DefaultProps>>;
}

// @public (undocumented)
export type RecordVersion = {
    rootVersion: number;
    subTypeVersion?: number;
};

// @public (undocumented)
export function reverseRecordsDiff(diff: RecordsDiff<any>): RecordsDiff<any>;

// @public (undocumented)
export interface SerializedSchema {
    recordVersions: Record<string, {
        version: number;
        subTypeVersions: Record<string, number>;
        subTypeKey: string;
    } | {
        version: number;
    }>;
    schemaVersion: number;
    storeVersion: number;
}

// @public
export function squashRecordDiffs<T extends BaseRecord>(diffs: RecordsDiff<T>[]): RecordsDiff<T>;

// @public
export class Store<R extends BaseRecord = BaseRecord, Props = unknown> {
    constructor(config: {
        initialData?: StoreSnapshot<R>;
        schema: StoreSchema<R, Props>;
        props: Props;
    });
    allRecords: () => R[];
    // (undocumented)
    applyDiff(diff: RecordsDiff<R>, runCallbacks?: boolean): void;
    clear: () => void;
    createComputedCache: <T, V extends R = R>(name: string, derive: (record: V) => T | undefined) => ComputedCache<T, V>;
    createSelectedComputedCache: <T, J, V extends R = R>(name: string, selector: (record: V) => T | undefined, derive: (input: T) => J | undefined) => ComputedCache<J, V>;
    deserialize: (snapshot: StoreSnapshot<R>) => void;
    // @internal (undocumented)
    ensureStoreIsUsable(): void;
    // (undocumented)
    extractingChanges(fn: () => void): RecordsDiff<R>;
    // (undocumented)
    _flushHistory(): void;
    get: <K extends ID<R>>(id: K) => RecFromId<K> | undefined;
    has: <K extends ID<R>>(id: K) => boolean;
    readonly history: Atom<number, RecordsDiff<R>>;
    // @internal (undocumented)
    isPossiblyCorrupted(): boolean;
    listen: (listener: StoreListener<R>) => () => void;
    // @internal (undocumented)
    markAsPossiblyCorrupted(): void;
    mergeRemoteChanges: (fn: () => void) => void;
    onAfterChange?: (prev: R, next: R) => void;
    onAfterCreate?: (record: R) => void;
    onAfterDelete?: (prev: R) => void;
    onBeforeDelete?: (prev: R) => void;
    // (undocumented)
    readonly props: Props;
    put: (records: R[], phaseOverride?: 'initialize') => void;
    readonly query: StoreQueries<R>;
    remove: (ids: ID<R>[]) => void;
    // (undocumented)
    readonly schema: StoreSchema<R, Props>;
    serialize: (filter?: ((record: R) => boolean) | undefined) => StoreSnapshot<R>;
    unsafeGetWithoutCapture: <K extends ID<R>>(id: K) => RecFromId<K> | undefined;
    update: <K extends ID<R>>(id: K, updater: (record: RecFromId<K>) => RecFromId<K>) => void;
    // (undocumented)
    validate(phase: 'createRecord' | 'initialize' | 'tests' | 'updateRecord'): void;
}

// @public (undocumented)
export type StoreError = {
    error: Error;
    phase: 'createRecord' | 'initialize' | 'tests' | 'updateRecord';
    recordBefore?: unknown;
    recordAfter: unknown;
    isExistingValidationIssue: boolean;
};

// @public
export type StoreListener<R extends BaseRecord> = (entry: HistoryEntry<R>) => void;

// @public (undocumented)
export class StoreSchema<R extends BaseRecord, P = unknown> {
    // (undocumented)
    static create<R extends BaseRecord, P = unknown>(types: {
        [TypeName in R['typeName']]: {
            createId: any;
        };
    }, options?: StoreSchemaOptions<R, P>): StoreSchema<R, P>;
    // @internal (undocumented)
    createIntegrityChecker(store: Store<R, P>): (() => void) | undefined;
    // (undocumented)
    get currentStoreVersion(): number;
    // @internal (undocumented)
    derivePresenceState(store: Store<R, P>): Signal<null | R> | undefined;
    // (undocumented)
    migratePersistedRecord(record: R, persistedSchema: SerializedSchema, direction?: 'down' | 'up'): MigrationResult<R>;
    // (undocumented)
    migrateStoreSnapshot(storeSnapshot: StoreSnapshot<R>, persistedSchema: SerializedSchema): MigrationResult<StoreSnapshot<R>>;
    // (undocumented)
    serialize(): SerializedSchema;
    // (undocumented)
    serializeEarliestVersion(): SerializedSchema;
    // (undocumented)
    readonly types: {
        [Record in R as Record['typeName']]: RecordType<R, any>;
    };
    // (undocumented)
    validateRecord(store: Store<R>, record: R, phase: 'createRecord' | 'initialize' | 'tests' | 'updateRecord', recordBefore: null | R): R;
}

// @public (undocumented)
export type StoreSchemaOptions<R extends BaseRecord, P> = {
    snapshotMigrations?: Migrations;
    onValidationFailure?: (data: {
        error: unknown;
        store: Store<R>;
        record: R;
        phase: 'createRecord' | 'initialize' | 'tests' | 'updateRecord';
        recordBefore: null | R;
    }) => R;
    createIntegrityChecker?: (store: Store<R, P>) => void;
    derivePresenceState?: (store: Store<R, P>) => Signal<null | R>;
};

// @public
export type StoreSnapshot<R extends BaseRecord> = Record<string, R>;

// @public (undocumented)
export type StoreValidator<R extends BaseRecord> = {
    validate: (record: unknown) => R;
};

// @public (undocumented)
export type StoreValidators<R extends BaseRecord> = {
    [K in R['typeName']]: StoreValidator<Extract<R, {
        typeName: K;
    }>>;
};

// (No @packageDocumentation comment for this package)