a48a55d3de
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.
10 KiB
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)