ab807afda3
This adds a store-level "operation end" event which fires at the end of atomic operations. It includes some other changes too: - The `SideEffectManager` now lives in & is a property of the store as `StoreSideEffects`. One benefit to this is that instead of overriding methods on the store to register side effects (meaning the store can only ever be used in one place) the store now calls directly into the side effect manager, which is responsible for dealing with any other callbacks - The history manager's "batch complete" event is gone, in favour of this new event. We were using the batch complete event for only one thing, calling `onChildrenChange` - which meant it wasn't getting called for undo/redo events, which aren't part of a batch. `onChildrenChange` is now called after each atomic store operation affecting children. I've also added a rough pin example which shows (kinda messily) how you might use the operation complete handler to traverse a graph of bindings and resolve constraints between them. ### Change Type - [x] `sdk` — Changes the tldraw SDK - [x] `feature` — New feature ### Release Notes #### Breaking changes `editor.registerBatchCompleteHandler` has been replaced with `editor.registerOperationCompleteHandler`
499 lines
17 KiB
Markdown
499 lines
17 KiB
Markdown
## API Report File for "@tldraw/store"
|
|
|
|
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
|
|
|
```ts
|
|
|
|
import { Atom } from '@tldraw/state';
|
|
import { Computed } from '@tldraw/state';
|
|
import { Result } from '@tldraw/utils';
|
|
|
|
// @public
|
|
export type AllRecords<T extends Store<any>> = ExtractR<ExtractRecordType<T>>;
|
|
|
|
// @public
|
|
export function assertIdType<R extends UnknownRecord>(id: string | undefined, type: RecordType<R, any>): asserts id is IdOf<R>;
|
|
|
|
// @public
|
|
export interface BaseRecord<TypeName extends string, Id extends RecordId<UnknownRecord>> {
|
|
// (undocumented)
|
|
readonly id: Id;
|
|
// (undocumented)
|
|
readonly typeName: TypeName;
|
|
}
|
|
|
|
// @public
|
|
export type CollectionDiff<T> = {
|
|
added?: Set<T>;
|
|
removed?: Set<T>;
|
|
};
|
|
|
|
// @public
|
|
export type ComputedCache<Data, R extends UnknownRecord> = {
|
|
get(id: IdOf<R>): Data | undefined;
|
|
};
|
|
|
|
// @public
|
|
export function createComputedCache<Context extends StoreContext<any>, Result, Record extends ContextRecordType<Context> = ContextRecordType<Context>>(name: string, derive: (context: Context, record: Record) => Result | undefined, isEqual?: (a: Record, b: Record) => boolean): {
|
|
get(context: Context, id: IdOf<Record>): Result | undefined;
|
|
};
|
|
|
|
// @internal (undocumented)
|
|
export function createEmptyRecordsDiff<R extends UnknownRecord>(): RecordsDiff<R>;
|
|
|
|
// @public
|
|
export function createMigrationIds<const ID extends string, const Versions extends Record<string, number>>(sequenceId: ID, versions: Versions): {
|
|
[K in keyof Versions]: `${ID}/${Versions[K]}`;
|
|
};
|
|
|
|
// @public
|
|
export function createMigrationSequence({ sequence, sequenceId, retroactive, }: {
|
|
retroactive?: boolean;
|
|
sequence: Array<Migration | StandaloneDependsOn>;
|
|
sequenceId: string;
|
|
}): MigrationSequence;
|
|
|
|
// @internal (undocumented)
|
|
export function createRecordMigrationSequence(opts: {
|
|
filter?: (record: UnknownRecord) => boolean;
|
|
recordType: string;
|
|
retroactive?: boolean;
|
|
sequence: Omit<Extract<Migration, {
|
|
scope: 'record';
|
|
}>, 'scope'>[];
|
|
sequenceId: string;
|
|
}): MigrationSequence;
|
|
|
|
// @public
|
|
export function createRecordType<R extends UnknownRecord>(typeName: R['typeName'], config: {
|
|
ephemeralKeys?: {
|
|
readonly [K in Exclude<keyof R, 'id' | 'typeName'>]: boolean;
|
|
};
|
|
scope: RecordScope;
|
|
validator?: StoreValidator<R>;
|
|
}): RecordType<R, keyof Omit<R, 'id' | 'typeName'>>;
|
|
|
|
// @public @deprecated (undocumented)
|
|
export function defineMigrations(opts: {
|
|
currentVersion?: number;
|
|
firstVersion?: number;
|
|
migrators?: Record<number, LegacyMigration>;
|
|
subTypeKey?: string;
|
|
subTypeMigrations?: Record<string, LegacyBaseMigrationsInfo>;
|
|
}): LegacyMigrations;
|
|
|
|
// @public
|
|
export function devFreeze<T>(object: T): T;
|
|
|
|
// @public
|
|
export type HistoryEntry<R extends UnknownRecord = UnknownRecord> = {
|
|
changes: RecordsDiff<R>;
|
|
source: ChangeSource;
|
|
};
|
|
|
|
// @public (undocumented)
|
|
export type IdOf<R extends UnknownRecord> = R['id'];
|
|
|
|
// @internal
|
|
export class IncrementalSetConstructor<T> {
|
|
constructor(
|
|
previousValue: Set<T>);
|
|
// @public
|
|
add(item: T): void;
|
|
// @public
|
|
get(): {
|
|
diff: CollectionDiff<T>;
|
|
value: Set<T>;
|
|
} | undefined;
|
|
// @public
|
|
remove(item: T): void;
|
|
}
|
|
|
|
// @internal
|
|
export function isRecordsDiffEmpty<T extends UnknownRecord>(diff: RecordsDiff<T>): boolean;
|
|
|
|
// @public (undocumented)
|
|
export type LegacyMigration<Before = any, After = any> = {
|
|
down: (newState: After) => Before;
|
|
up: (oldState: Before) => After;
|
|
};
|
|
|
|
// @public (undocumented)
|
|
export interface LegacyMigrations extends LegacyBaseMigrationsInfo {
|
|
// (undocumented)
|
|
subTypeKey?: string;
|
|
// (undocumented)
|
|
subTypeMigrations?: Record<string, LegacyBaseMigrationsInfo>;
|
|
}
|
|
|
|
// @public (undocumented)
|
|
export type Migration = {
|
|
readonly dependsOn?: readonly MigrationId[] | undefined;
|
|
readonly id: MigrationId;
|
|
} & ({
|
|
readonly down?: (newState: SerializedStore<UnknownRecord>) => SerializedStore<UnknownRecord> | void;
|
|
readonly scope: 'store';
|
|
readonly up: (oldState: SerializedStore<UnknownRecord>) => SerializedStore<UnknownRecord> | void;
|
|
} | {
|
|
readonly down?: (newState: UnknownRecord) => UnknownRecord | void;
|
|
readonly filter?: (record: UnknownRecord) => boolean;
|
|
readonly scope: 'record';
|
|
readonly up: (oldState: UnknownRecord) => UnknownRecord | void;
|
|
});
|
|
|
|
// @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 MigrationId = `${string}/${number}`;
|
|
|
|
// @public (undocumented)
|
|
export type MigrationResult<T> = {
|
|
reason: MigrationFailureReason;
|
|
type: 'error';
|
|
} | {
|
|
type: 'success';
|
|
value: T;
|
|
};
|
|
|
|
// @public (undocumented)
|
|
export interface MigrationSequence {
|
|
retroactive: boolean;
|
|
// (undocumented)
|
|
sequence: Migration[];
|
|
// (undocumented)
|
|
sequenceId: string;
|
|
}
|
|
|
|
// @internal (undocumented)
|
|
export function parseMigrationId(id: MigrationId): {
|
|
sequenceId: string;
|
|
version: number;
|
|
};
|
|
|
|
// @public (undocumented)
|
|
export type RecordId<R extends UnknownRecord> = string & {
|
|
__type__: R;
|
|
};
|
|
|
|
// @public
|
|
export type RecordsDiff<R extends UnknownRecord> = {
|
|
added: Record<IdOf<R>, R>;
|
|
removed: Record<IdOf<R>, R>;
|
|
updated: Record<IdOf<R>, [from: R, to: R]>;
|
|
};
|
|
|
|
// @public
|
|
export class RecordType<R extends UnknownRecord, RequiredProperties extends keyof Omit<R, 'id' | 'typeName'>> {
|
|
constructor(
|
|
typeName: R['typeName'], config: {
|
|
readonly createDefaultProperties: () => Exclude<OmitMeta<R>, RequiredProperties>;
|
|
readonly ephemeralKeys?: {
|
|
readonly [K in Exclude<keyof R, 'id' | 'typeName'>]: boolean;
|
|
};
|
|
readonly scope?: RecordScope;
|
|
readonly validator?: StoreValidator<R>;
|
|
});
|
|
clone(record: R): R;
|
|
create(properties: Pick<R, RequiredProperties> & Omit<Partial<R>, RequiredProperties>): R;
|
|
// @deprecated
|
|
createCustomId(id: string): IdOf<R>;
|
|
// (undocumented)
|
|
readonly createDefaultProperties: () => Exclude<OmitMeta<R>, RequiredProperties>;
|
|
createId(customUniquePart?: string): IdOf<R>;
|
|
// (undocumented)
|
|
readonly ephemeralKeys?: {
|
|
readonly [K in Exclude<keyof R, 'id' | 'typeName'>]: boolean;
|
|
};
|
|
// (undocumented)
|
|
readonly ephemeralKeySet: ReadonlySet<string>;
|
|
isId(id?: string): id is IdOf<R>;
|
|
isInstance: (record?: UnknownRecord) => record is R;
|
|
parseId(id: IdOf<R>): string;
|
|
// (undocumented)
|
|
readonly scope: RecordScope;
|
|
readonly typeName: R['typeName'];
|
|
validate(record: unknown, recordBefore?: R): R;
|
|
// (undocumented)
|
|
readonly validator: StoreValidator<R>;
|
|
withDefaultProperties<DefaultProps extends Omit<Partial<R>, 'id' | 'typeName'>>(createDefaultProperties: () => DefaultProps): RecordType<R, Exclude<RequiredProperties, keyof DefaultProps>>;
|
|
}
|
|
|
|
// @public (undocumented)
|
|
export function reverseRecordsDiff(diff: RecordsDiff<any>): RecordsDiff<any>;
|
|
|
|
// @public (undocumented)
|
|
export type SerializedSchema = SerializedSchemaV1 | SerializedSchemaV2;
|
|
|
|
// @public (undocumented)
|
|
export interface SerializedSchemaV1 {
|
|
recordVersions: Record<string, {
|
|
subTypeKey: string;
|
|
subTypeVersions: Record<string, number>;
|
|
version: number;
|
|
} | {
|
|
version: number;
|
|
}>;
|
|
schemaVersion: 1;
|
|
storeVersion: number;
|
|
}
|
|
|
|
// @public (undocumented)
|
|
export interface SerializedSchemaV2 {
|
|
// (undocumented)
|
|
schemaVersion: 2;
|
|
// (undocumented)
|
|
sequences: {
|
|
[sequenceId: string]: number;
|
|
};
|
|
}
|
|
|
|
// @public
|
|
export type SerializedStore<R extends UnknownRecord> = Record<IdOf<R>, R>;
|
|
|
|
// @public
|
|
export function squashRecordDiffs<T extends UnknownRecord>(diffs: RecordsDiff<T>[]): RecordsDiff<T>;
|
|
|
|
// @internal
|
|
export function squashRecordDiffsMutable<T extends UnknownRecord>(target: RecordsDiff<T>, diffs: RecordsDiff<T>[]): void;
|
|
|
|
// @public (undocumented)
|
|
export type StandaloneDependsOn = {
|
|
readonly dependsOn: readonly MigrationId[];
|
|
};
|
|
|
|
// @public
|
|
export class Store<R extends UnknownRecord = UnknownRecord, Props = unknown> {
|
|
constructor(config: {
|
|
schema: StoreSchema<R, Props>;
|
|
initialData?: SerializedStore<R>;
|
|
id?: string;
|
|
props: Props;
|
|
});
|
|
// @internal (undocumented)
|
|
addHistoryInterceptor(fn: (entry: HistoryEntry<R>, source: ChangeSource) => void): () => void;
|
|
allRecords: () => R[];
|
|
// (undocumented)
|
|
applyDiff(diff: RecordsDiff<R>, { runCallbacks, ignoreEphemeralKeys, }?: {
|
|
ignoreEphemeralKeys?: boolean;
|
|
runCallbacks?: boolean;
|
|
}): void;
|
|
// @internal (undocumented)
|
|
atomic<T>(fn: () => T, runCallbacks?: boolean): T;
|
|
clear: () => void;
|
|
createComputedCache: <Result, Record extends R = R>(name: string, derive: (record: Record) => Result | undefined, isEqual?: ((a: Record, b: Record) => boolean) | undefined) => ComputedCache<Result, Record>;
|
|
createSelectedComputedCache: <Selection, Result, Record extends R = R>(name: string, selector: (record: Record) => Selection | undefined, derive: (input: Selection) => Result | undefined) => ComputedCache<Result, Record>;
|
|
// @internal (undocumented)
|
|
ensureStoreIsUsable(): void;
|
|
extractingChanges(fn: () => void): RecordsDiff<R>;
|
|
filterChangesByScope(change: RecordsDiff<R>, scope: RecordScope): {
|
|
added: { [K in IdOf<R>]: R; };
|
|
removed: { [K in IdOf<R>]: R; };
|
|
updated: { [K_1 in IdOf<R>]: [from: R, to: R]; };
|
|
} | null;
|
|
// (undocumented)
|
|
_flushHistory(): void;
|
|
get: <K extends IdOf<R>>(id: K) => RecFromId<K> | undefined;
|
|
getSnapshot(scope?: 'all' | RecordScope): StoreSnapshot<R>;
|
|
has: <K extends IdOf<R>>(id: K) => boolean;
|
|
readonly history: Atom<number, RecordsDiff<R>>;
|
|
readonly id: string;
|
|
// @internal (undocumented)
|
|
isPossiblyCorrupted(): boolean;
|
|
listen: (onHistory: StoreListener<R>, filters?: Partial<StoreListenerFilters>) => () => void;
|
|
loadSnapshot(snapshot: StoreSnapshot<R>): void;
|
|
// @internal (undocumented)
|
|
markAsPossiblyCorrupted(): void;
|
|
mergeRemoteChanges: (fn: () => void) => void;
|
|
migrateSnapshot(snapshot: StoreSnapshot<R>): StoreSnapshot<R>;
|
|
// (undocumented)
|
|
readonly props: Props;
|
|
put: (records: R[], phaseOverride?: 'initialize') => void;
|
|
readonly query: StoreQueries<R>;
|
|
remove: (ids: IdOf<R>[]) => void;
|
|
// (undocumented)
|
|
readonly schema: StoreSchema<R, Props>;
|
|
// (undocumented)
|
|
readonly scopedTypes: {
|
|
readonly [K in RecordScope]: ReadonlySet<R['typeName']>;
|
|
};
|
|
serialize: (scope?: 'all' | RecordScope) => SerializedStore<R>;
|
|
// (undocumented)
|
|
readonly sideEffects: StoreSideEffects<R>;
|
|
unsafeGetWithoutCapture: <K extends IdOf<R>>(id: K) => RecFromId<K> | undefined;
|
|
update: <K extends IdOf<R>>(id: K, updater: (record: RecFromId<K>) => RecFromId<K>) => void;
|
|
// (undocumented)
|
|
validate(phase: 'createRecord' | 'initialize' | 'tests' | 'updateRecord'): void;
|
|
}
|
|
|
|
// @public (undocumented)
|
|
export type StoreAfterChangeHandler<R extends UnknownRecord> = (prev: R, next: R, source: 'remote' | 'user') => void;
|
|
|
|
// @public (undocumented)
|
|
export type StoreAfterCreateHandler<R extends UnknownRecord> = (record: R, source: 'remote' | 'user') => void;
|
|
|
|
// @public (undocumented)
|
|
export type StoreAfterDeleteHandler<R extends UnknownRecord> = (record: R, source: 'remote' | 'user') => void;
|
|
|
|
// @public (undocumented)
|
|
export type StoreBeforeChangeHandler<R extends UnknownRecord> = (prev: R, next: R, source: 'remote' | 'user') => R;
|
|
|
|
// @public (undocumented)
|
|
export type StoreBeforeCreateHandler<R extends UnknownRecord> = (record: R, source: 'remote' | 'user') => R;
|
|
|
|
// @public (undocumented)
|
|
export type StoreBeforeDeleteHandler<R extends UnknownRecord> = (record: R, source: 'remote' | 'user') => false | void;
|
|
|
|
// @public (undocumented)
|
|
export type StoreError = {
|
|
error: Error;
|
|
isExistingValidationIssue: boolean;
|
|
phase: 'createRecord' | 'initialize' | 'tests' | 'updateRecord';
|
|
recordAfter: unknown;
|
|
recordBefore?: unknown;
|
|
};
|
|
|
|
// @public
|
|
export type StoreListener<R extends UnknownRecord> = (entry: HistoryEntry<R>) => void;
|
|
|
|
// @public (undocumented)
|
|
export type StoreOperationCompleteHandler = (source: 'remote' | 'user') => void;
|
|
|
|
// @public (undocumented)
|
|
export class StoreSchema<R extends UnknownRecord, P = unknown> {
|
|
// (undocumented)
|
|
static create<R extends UnknownRecord, 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)
|
|
getMigrationsSince(persistedSchema: SerializedSchema): Result<Migration[], string>;
|
|
// @internal (undocumented)
|
|
getType(typeName: string): RecordType<R, any>;
|
|
// (undocumented)
|
|
migratePersistedRecord(record: R, persistedSchema: SerializedSchema, direction?: 'down' | 'up'): MigrationResult<R>;
|
|
// (undocumented)
|
|
migrateStoreSnapshot(snapshot: StoreSnapshot<R>): MigrationResult<SerializedStore<R>>;
|
|
// (undocumented)
|
|
readonly migrations: Record<string, MigrationSequence>;
|
|
// (undocumented)
|
|
serialize(): SerializedSchemaV2;
|
|
// @deprecated (undocumented)
|
|
serializeEarliestVersion(): SerializedSchema;
|
|
// (undocumented)
|
|
readonly sortedMigrations: readonly Migration[];
|
|
// (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 UnknownRecord, P> = {
|
|
createIntegrityChecker?: (store: Store<R, P>) => void;
|
|
onValidationFailure?: (data: {
|
|
error: unknown;
|
|
phase: 'createRecord' | 'initialize' | 'tests' | 'updateRecord';
|
|
record: R;
|
|
recordBefore: null | R;
|
|
store: Store<R>;
|
|
}) => R;
|
|
migrations?: MigrationSequence[];
|
|
};
|
|
|
|
// @public
|
|
export class StoreSideEffects<R extends UnknownRecord> {
|
|
constructor(store: Store<R>);
|
|
// @internal (undocumented)
|
|
handleAfterChange(prev: R, next: R, source: 'remote' | 'user'): void;
|
|
// @internal (undocumented)
|
|
handleAfterCreate(record: R, source: 'remote' | 'user'): void;
|
|
// @internal (undocumented)
|
|
handleAfterDelete(record: R, source: 'remote' | 'user'): void;
|
|
// @internal (undocumented)
|
|
handleBeforeChange(prev: R, next: R, source: 'remote' | 'user'): R;
|
|
// @internal (undocumented)
|
|
handleBeforeCreate(record: R, source: 'remote' | 'user'): R;
|
|
// @internal (undocumented)
|
|
handleBeforeDelete(record: R, source: 'remote' | 'user'): boolean;
|
|
// @internal (undocumented)
|
|
handleOperationComplete(source: 'remote' | 'user'): void;
|
|
// @internal (undocumented)
|
|
isEnabled(): boolean;
|
|
// @internal
|
|
register(handlersByType: {
|
|
[T in R as T['typeName']]?: {
|
|
afterChange?: StoreAfterChangeHandler<T>;
|
|
afterCreate?: StoreAfterCreateHandler<T>;
|
|
afterDelete?: StoreAfterDeleteHandler<T>;
|
|
beforeChange?: StoreBeforeChangeHandler<T>;
|
|
beforeCreate?: StoreBeforeCreateHandler<T>;
|
|
beforeDelete?: StoreBeforeDeleteHandler<T>;
|
|
};
|
|
}): () => void;
|
|
registerAfterChangeHandler<T extends R['typeName']>(typeName: T, handler: StoreAfterChangeHandler<R & {
|
|
typeName: T;
|
|
}>): () => void;
|
|
registerAfterCreateHandler<T extends R['typeName']>(typeName: T, handler: StoreAfterCreateHandler<R & {
|
|
typeName: T;
|
|
}>): () => void;
|
|
registerAfterDeleteHandler<T extends R['typeName']>(typeName: T, handler: StoreAfterDeleteHandler<R & {
|
|
typeName: T;
|
|
}>): () => void;
|
|
registerBeforeChangeHandler<T extends R['typeName']>(typeName: T, handler: StoreBeforeChangeHandler<R & {
|
|
typeName: T;
|
|
}>): () => void;
|
|
registerBeforeCreateHandler<T extends R['typeName']>(typeName: T, handler: StoreBeforeCreateHandler<R & {
|
|
typeName: T;
|
|
}>): () => void;
|
|
registerBeforeDeleteHandler<T extends R['typeName']>(typeName: T, handler: StoreBeforeDeleteHandler<R & {
|
|
typeName: T;
|
|
}>): () => void;
|
|
registerOperationCompleteHandler(handler: StoreOperationCompleteHandler): () => void;
|
|
// @internal (undocumented)
|
|
setIsEnabled(enabled: boolean): void;
|
|
}
|
|
|
|
// @public (undocumented)
|
|
export type StoreSnapshot<R extends UnknownRecord> = {
|
|
schema: SerializedSchema;
|
|
store: SerializedStore<R>;
|
|
};
|
|
|
|
// @public (undocumented)
|
|
export type StoreValidator<R extends UnknownRecord> = {
|
|
validate: (record: unknown) => R;
|
|
validateUsingKnownGoodVersion?: (knownGoodVersion: R, record: unknown) => R;
|
|
};
|
|
|
|
// @public (undocumented)
|
|
export type StoreValidators<R extends UnknownRecord> = {
|
|
[K in R['typeName']]: StoreValidator<Extract<R, {
|
|
typeName: K;
|
|
}>>;
|
|
};
|
|
|
|
// @public (undocumented)
|
|
export type UnknownRecord = BaseRecord<string, RecordId<UnknownRecord>>;
|
|
|
|
// (No @packageDocumentation comment for this package)
|
|
|
|
```
|