import { Atom } from '@tldraw/state';
import { Computed } from '@tldraw/state';
import { Expand } from '@tldraw/utils';
import { Result } from '@tldraw/utils';
import { Signal } from '@tldraw/state';
import { UNINITIALIZED } from '@tldraw/state';

/**
 * Assert whether an id correspond to a record type.
 *
 * @example
 *
 * ```ts
 * assertIdType(myId, "shape")
 * ```
 *
 * @param id - The id to check.
 * @param type - The type of the record.
 * @public
 */
export declare function assertIdType<R extends UnknownRecord>(id: string | undefined, type: RecordType<R, any>): asserts id is IdOf<R>;

/**
 * A drop-in replacement for Map that stores values in atoms and can be used in reactive contexts.
 * @public
 */
export declare class AtomMap<K, V> implements Map<K, V> {
    private readonly name;
    private atoms;
    /**
     * Creates a new AtomMap instance.
     *
     * name - A unique name for this map, used for atom identification
     * entries - Optional initial entries to populate the map with
     * @example
     * ```ts
     * // Create an empty map
     * const map = new AtomMap('userMap')
     *
     * // Create a map with initial data
     * const initialData: [string, number][] = [['a', 1], ['b', 2]]
     * const mapWithData = new AtomMap('numbersMap', initialData)
     * ```
     */
    constructor(name: string, entries?: Iterable<readonly [K, V]>);
    /* Excluded from this release type: getAtom */
    /**
     * Gets the value associated with a key. Returns undefined if the key doesn't exist.
     * This method is reactive and will cause reactive contexts to update when the value changes.
     *
     * @param key - The key to retrieve the value for
     * @returns The value associated with the key, or undefined if not found
     * @example
     * ```ts
     * const map = new AtomMap('myMap')
     * map.set('name', 'Alice')
     * console.log(map.get('name')) // 'Alice'
     * console.log(map.get('missing')) // undefined
     * ```
     */
    get(key: K): undefined | V;
    /**
     * Gets the value associated with a key without creating reactive dependencies.
     * This method will not cause reactive contexts to update when the value changes.
     *
     * @param key - The key to retrieve the value for
     * @returns The value associated with the key, or undefined if not found
     * @example
     * ```ts
     * const map = new AtomMap('myMap')
     * map.set('count', 42)
     * const value = map.__unsafe__getWithoutCapture('count') // No reactive subscription
     * ```
     */
    __unsafe__getWithoutCapture(key: K): undefined | V;
    /**
     * Checks whether a key exists in the map.
     * This method is reactive and will cause reactive contexts to update when keys are added or removed.
     *
     * @param key - The key to check for
     * @returns True if the key exists in the map, false otherwise
     * @example
     * ```ts
     * const map = new AtomMap('myMap')
     * console.log(map.has('name')) // false
     * map.set('name', 'Alice')
     * console.log(map.has('name')) // true
     * ```
     */
    has(key: K): boolean;
    /**
     * Checks whether a key exists in the map without creating reactive dependencies.
     * This method will not cause reactive contexts to update when keys are added or removed.
     *
     * @param key - The key to check for
     * @returns True if the key exists in the map, false otherwise
     * @example
     * ```ts
     * const map = new AtomMap('myMap')
     * map.set('active', true)
     * const exists = map.__unsafe__hasWithoutCapture('active') // No reactive subscription
     * ```
     */
    __unsafe__hasWithoutCapture(key: K): boolean;
    /**
     * Sets a value for the given key. If the key already exists, its value is updated.
     * If the key doesn't exist, a new entry is created.
     *
     * @param key - The key to set the value for
     * @param value - The value to associate with the key
     * @returns This AtomMap instance for method chaining
     * @example
     * ```ts
     * const map = new AtomMap('myMap')
     * map.set('name', 'Alice').set('age', 30)
     * ```
     */
    set(key: K, value: V): this;
    /**
     * Updates an existing value using an updater function.
     *
     * @param key - The key of the value to update
     * @param updater - A function that receives the current value and returns the new value
     * @throws Error if the key doesn't exist in the map
     * @example
     * ```ts
     * const map = new AtomMap('myMap')
     * map.set('count', 5)
     * map.update('count', count => count + 1) // count is now 6
     * ```
     */
    update(key: K, updater: (value: V) => V): void;
    /**
     * Removes a key-value pair from the map.
     *
     * @param key - The key to remove
     * @returns True if the key existed and was removed, false if it didn't exist
     * @example
     * ```ts
     * const map = new AtomMap('myMap')
     * map.set('temp', 'value')
     * console.log(map.delete('temp')) // true
     * console.log(map.delete('missing')) // false
     * ```
     */
    delete(key: K): boolean;
    /**
     * Removes multiple key-value pairs from the map in a single transaction.
     *
     * @param keys - An iterable of keys to remove
     * @returns An array of [key, value] pairs that were actually deleted
     * @example
     * ```ts
     * const map = new AtomMap('myMap')
     * map.set('a', 1).set('b', 2).set('c', 3)
     * const deleted = map.deleteMany(['a', 'c', 'missing'])
     * console.log(deleted) // [['a', 1], ['c', 3]]
     * ```
     */
    deleteMany(keys: Iterable<K>): [K, V][];
    /**
     * Removes all key-value pairs from the map.
     *
     * @example
     * ```ts
     * const map = new AtomMap('myMap')
     * map.set('a', 1).set('b', 2)
     * map.clear()
     * console.log(map.size) // 0
     * ```
     */
    clear(): void;
    /**
     * Returns an iterator that yields [key, value] pairs for each entry in the map.
     * This method is reactive and will cause reactive contexts to update when entries change.
     *
     * @returns A generator that yields [key, value] tuples
     * @example
     * ```ts
     * const map = new AtomMap('myMap')
     * map.set('a', 1).set('b', 2)
     * for (const [key, value] of map.entries()) {
     *   console.log(`${key}: ${value}`)
     * }
     * ```
     */
    entries(): Generator<[K, V], undefined, unknown>;
    /**
     * Returns an iterator that yields all keys in the map.
     * This method is reactive and will cause reactive contexts to update when keys change.
     *
     * @returns A generator that yields keys
     * @example
     * ```ts
     * const map = new AtomMap('myMap')
     * map.set('name', 'Alice').set('age', 30)
     * for (const key of map.keys()) {
     *   console.log(key) // 'name', 'age'
     * }
     * ```
     */
    keys(): Generator<K, undefined, unknown>;
    /**
     * Returns an iterator that yields all values in the map.
     * This method is reactive and will cause reactive contexts to update when values change.
     *
     * @returns A generator that yields values
     * @example
     * ```ts
     * const map = new AtomMap('myMap')
     * map.set('name', 'Alice').set('age', 30)
     * for (const value of map.values()) {
     *   console.log(value) // 'Alice', 30
     * }
     * ```
     */
    values(): Generator<V, undefined, unknown>;
    /**
     * The number of key-value pairs in the map.
     * This property is reactive and will cause reactive contexts to update when the size changes.
     *
     * @returns The number of entries in the map
     * @example
     * ```ts
     * const map = new AtomMap('myMap')
     * console.log(map.size) // 0
     * map.set('a', 1)
     * console.log(map.size) // 1
     * ```
     */
    get size(): number;
    /**
     * Executes a provided function once for each key-value pair in the map.
     * This method is reactive and will cause reactive contexts to update when entries change.
     *
     * @param callbackfn - Function to execute for each entry
     *   - value - The value of the current entry
     *   - key - The key of the current entry
     *   - map - The AtomMap being traversed
     * @param thisArg - Value to use as `this` when executing the callback
     * @example
     * ```ts
     * const map = new AtomMap('myMap')
     * map.set('a', 1).set('b', 2)
     * map.forEach((value, key) => {
     *   console.log(`${key} = ${value}`)
     * })
     * ```
     */
    forEach(callbackfn: (value: V, key: K, map: AtomMap<K, V>) => void, thisArg?: any): void;
    /**
     * Returns the default iterator for the map, which is the same as entries().
     * This allows the map to be used in for...of loops and other iterable contexts.
     *
     * @returns The same iterator as entries()
     * @example
     * ```ts
     * const map = new AtomMap('myMap')
     * map.set('a', 1).set('b', 2)
     *
     * // These are equivalent:
     * for (const [key, value] of map) {
     *   console.log(`${key}: ${value}`)
     * }
     *
     * for (const [key, value] of map.entries()) {
     *   console.log(`${key}: ${value}`)
     * }
     * ```
     */
    [Symbol.iterator](): Generator<[K, V], undefined, unknown>;
    /**
     * The string tag used by Object.prototype.toString for this class.
     *
     * @example
     * ```ts
     * const map = new AtomMap('myMap')
     * console.log(Object.prototype.toString.call(map)) // '[object AtomMap]'
     * ```
     */
    [Symbol.toStringTag]: string;
}

/**
 * A drop-in replacement for Set that stores values in atoms and can be used in reactive contexts.
 * @public
 */
export declare class AtomSet<T> {
    private readonly name;
    private readonly map;
    constructor(name: string, keys?: Iterable<T>);
    add(value: T): this;
    clear(): void;
    delete(value: T): boolean;
    forEach(callbackfn: (value: T, value2: T, set: AtomSet<T>) => void, thisArg?: any): void;
    has(value: T): boolean;
    get size(): number;
    entries(): Generator<[T, T], undefined, unknown>;
    keys(): Generator<T, undefined, unknown>;
    values(): Generator<T, undefined, unknown>;
    [Symbol.iterator](): Generator<T, undefined, unknown>;
    [Symbol.toStringTag]: string;
}

/**
 * The base record interface that all records in the store must extend.
 * This interface provides the fundamental structure required for all records: a unique ID and a type name.
 * The type parameters ensure type safety and prevent mixing of different record types.
 *
 * @example
 * ```ts
 * // Define a Book record that extends BaseRecord
 * interface Book extends BaseRecord<'book', RecordId<Book>> {
 *   title: string
 *   author: string
 *   publishedYear: number
 * }
 *
 * // Define an Author record
 * interface Author extends BaseRecord<'author', RecordId<Author>> {
 *   name: string
 *   birthYear: number
 * }
 *
 * // Usage with RecordType
 * const Book = createRecordType<Book>('book', { scope: 'document' })
 * const book = Book.create({
 *   title: '1984',
 *   author: 'George Orwell',
 *   publishedYear: 1949
 * })
 * // Results in: { id: 'book:abc123', typeName: 'book', title: '1984', ... }
 * ```
 *
 * @public
 */
export declare interface BaseRecord<TypeName extends string, Id extends RecordId<UnknownRecord>> {
    readonly id: Id;
    readonly typeName: TypeName;
}

/**
 * The source of a change to the store.
 * - `'user'` - Changes originating from local user actions
 * - `'remote'` - Changes originating from remote synchronization
 *
 * @public
 */
export declare type ChangeSource = 'remote' | 'user';

/**
 * A diff describing the changes to a collection.
 *
 * @example
 * ```ts
 * const diff: CollectionDiff<string> = {
 *   added: new Set(['newItem']),
 *   removed: new Set(['oldItem'])
 * }
 * ```
 *
 * @public
 */
export declare interface CollectionDiff<T> {
    /** Items that were added to the collection */
    added?: Set<T>;
    /** Items that were removed from the collection */
    removed?: Set<T>;
}

/**
 * A computed cache that stores derived data for records.
 * The cache automatically updates when underlying records change and cleans up when records are deleted.
 *
 * @example
 * ```ts
 * const expensiveCache = store.createComputedCache(
 *   'expensive',
 *   (book: Book) => performExpensiveCalculation(book)
 * )
 *
 * const result = expensiveCache.get(bookId)
 * ```
 *
 * @public
 */
export declare interface ComputedCache<Data, R extends UnknownRecord> {
    /**
     * Get the cached data for a record by its ID.
     *
     * @param id - The ID of the record
     * @returns The cached data or undefined if the record doesn't exist
     */
    get(id: IdOf<R>): Data | undefined;
}

/**
 * Create a computed cache that works with any StoreObject (store or object containing a store).
 * This is a standalone version of Store.createComputedCache that can work with multiple store instances.
 *
 * @example
 * ```ts
 * const expensiveCache = createComputedCache(
 *   'expensiveData',
 *   (context: { store: Store<Book> }, book: Book) => {
 *     return performExpensiveCalculation(book)
 *   }
 * )
 *
 * // Use with different store instances
 * const result1 = expensiveCache.get(storeObject1, bookId)
 * const result2 = expensiveCache.get(storeObject2, bookId)
 * ```
 *
 * @param name - A unique name for the cache (used for debugging)
 * @param derive - Function that derives a value from the context and record
 * @param opts - Optional configuration for equality checks
 * @returns A cache that can be used with multiple store instances
 * @public
 */
export declare function createComputedCache<Context extends StoreObject<any>, Result, Record extends StoreObjectRecordType<Context> = StoreObjectRecordType<Context>>(name: string, derive: (context: Context, record: Record) => Result | undefined, opts?: CreateComputedCacheOpts<Result, Record>): {
    get(context: Context, id: IdOf<Record>): Result | undefined;
};

/**
 * Options for creating a computed cache.
 *
 * @example
 * ```ts
 * const options: CreateComputedCacheOpts<string[], Book> = {
 *   areRecordsEqual: (a, b) => a.title === b.title,
 *   areResultsEqual: (a, b) => JSON.stringify(a) === JSON.stringify(b)
 * }
 * ```
 *
 * @public
 */
export declare interface CreateComputedCacheOpts<Data, R extends UnknownRecord> {
    /** Custom equality function for comparing records */
    areRecordsEqual?(a: R, b: R): boolean;
    /** Custom equality function for comparing results */
    areResultsEqual?(a: Data, b: Data): boolean;
}

/* Excluded from this release type: createEmptyRecordsDiff */

/**
 * Creates a named set of migration IDs from version numbers and a sequence ID.
 *
 * This utility function helps generate properly formatted migration IDs that follow
 * the required `sequenceId/version` pattern. It takes a sequence ID and a record
 * of named versions, returning migration IDs that can be used in migration definitions.
 *
 * See the [migration guide](https://tldraw.dev/docs/persistence#Migrations) for more info on how to use this API.
 * @param sequenceId - The sequence identifier (e.g., 'com.myapp.book')
 * @param versions - Record mapping version names to numbers
 * @returns Record mapping version names to properly formatted migration IDs
 * @example
 * ```ts
 * const migrationIds = createMigrationIds('com.myapp.book', {
 *   addGenre: 1,
 *   addPublisher: 2,
 *   removeOldField: 3
 * })
 * // Result: {
 * //   addGenre: 'com.myapp.book/1',
 * //   addPublisher: 'com.myapp.book/2',
 * //   removeOldField: 'com.myapp.book/3'
 * // }
 * ```
 * @public
 */
export declare function createMigrationIds<const ID extends string, const Versions extends Record<string, number>>(sequenceId: ID, versions: Versions): {
    [K in keyof Versions]: `${ID}/${Versions[K]}`;
};

/**
 * Creates a migration sequence that defines how to transform data as your schema evolves.
 *
 * A migration sequence contains a series of migrations that are applied in order to transform
 * data from older versions to newer versions. Each migration is identified by a unique ID
 * and can operate at either the record level (transforming individual records) or store level
 * (transforming the entire store structure).
 *
 * See the [migration guide](https://tldraw.dev/docs/persistence#Migrations) for more info on how to use this API.
 * @param options - Configuration for the migration sequence
 *   - sequenceId - Unique identifier for this migration sequence (e.g., 'com.myapp.book')
 *   - sequence - Array of migrations or dependency declarations to include in the sequence
 *   - retroactive - Whether migrations should apply to snapshots created before this sequence was added (defaults to true)
 * @returns A validated migration sequence that can be included in a store schema
 * @example
 * ```ts
 * const bookMigrations = createMigrationSequence({
 *   sequenceId: 'com.myapp.book',
 *   sequence: [
 *     {
 *       id: 'com.myapp.book/1',
 *       scope: 'record',
 *       up: (record) => ({ ...record, newField: 'default' })
 *     }
 *   ]
 * })
 * ```
 * @public
 */
export declare function createMigrationSequence({ sequence, sequenceId, retroactive }: {
    retroactive?: boolean;
    sequence: Array<Migration | StandaloneDependsOn>;
    sequenceId: string;
}): MigrationSequence;

/* Excluded from this release type: createRecordMigrationSequence */

/**
 * Creates a new RecordType with the specified configuration.
 *
 * This factory function creates a RecordType that can be used to create, validate, and manage
 * records of a specific type within a store. The resulting RecordType can be extended with
 * default properties using the withDefaultProperties method.
 *
 * @example
 * ```ts
 * interface BookRecord extends BaseRecord<'book', RecordId<BookRecord>> {
 *   title: string
 *   author: string
 *   inStock: boolean
 * }
 *
 * const Book = createRecordType<BookRecord>('book', {
 *   scope: 'document',
 *   validator: bookValidator
 * })
 * ```
 *
 * @param typeName - The unique type name for this record type
 * @param config - Configuration object containing validator, scope, and ephemeral keys
 * @returns A new RecordType instance for creating and managing records
 * @public
 */
export declare 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'>>;

/**
 * Freeze an object when in development mode. Copied from
 * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze
 *
 * @example
 *
 * ```ts
 * const frozen = devFreeze({ a: 1 })
 * ```
 *
 * @param object - The object to freeze.
 * @returns The frozen object when in development mode, or else the object when in other modes.
 * @public
 */
export declare function devFreeze<T>(object: T): T;

/**
 * An entry containing changes that originated either by user actions or remote changes.
 * History entries are used to track and replay changes to the store.
 *
 * @example
 * ```ts
 * const entry: HistoryEntry<Book> = {
 *   changes: {
 *     added: { 'book:123': bookRecord },
 *     updated: {},
 *     removed: {}
 *   },
 *   source: 'user'
 * }
 * ```
 *
 * @public
 */
export declare interface HistoryEntry<R extends UnknownRecord = UnknownRecord> {
    /** The changes that occurred in this history entry */
    changes: RecordsDiff<R>;
    /** The source of these changes */
    source: ChangeSource;
}

/**
 * Utility type that extracts the ID type from a record type.
 * This is useful when you need to work with record IDs without having the full record type.
 *
 * @example
 * ```ts
 * interface Book extends BaseRecord<'book', RecordId<Book>> {
 *   title: string
 *   author: string
 * }
 *
 * // Extract the ID type from the Book record
 * type BookId = IdOf<Book> // RecordId<Book>
 *
 * function findBook(id: IdOf<Book>): Book | undefined {
 *   return store.get(id)
 * }
 * ```
 *
 * @public
 */
export declare type IdOf<R extends UnknownRecord> = R['id'];

/* Excluded from this release type: IncrementalSetConstructor */

/**
 * Checks whether a RecordsDiff contains any changes. A diff is considered empty
 * if it has no added, updated, or removed records.
 *
 * @param diff - The diff to check
 * @returns True if the diff contains no changes, false otherwise
 * @example
 * ```ts
 * const emptyDiff = createEmptyRecordsDiff<Book>()
 * console.log(isRecordsDiffEmpty(emptyDiff)) // true
 *
 * const nonEmptyDiff: RecordsDiff<Book> = {
 *   added: { 'book:1': someBook },
 *   updated: {},
 *   removed: {}
 * }
 * console.log(isRecordsDiffEmpty(nonEmptyDiff)) // false
 * ```
 *
 * @public
 */
export declare function isRecordsDiffEmpty<T extends UnknownRecord>(diff: RecordsDiff<T>): boolean;

/**
 * Base interface for legacy migration information.
 *
 * Contains the basic structure used by the legacy migration system, including version
 * range information and the migration functions indexed by version number. This is
 * maintained for backward compatibility with older migration definitions.
 * @public
 */
export declare interface LegacyBaseMigrationsInfo {
    firstVersion: number;
    currentVersion: number;
    migrators: {
        [version: number]: LegacyMigration;
    };
}

/**
 * Legacy migration interface for backward compatibility.
 *
 * This interface represents the old migration format that included both `up` and `down`
 * transformation functions. While still supported, new code should use the `Migration`
 * type which provides more flexibility and better integration with the current system.
 * @public
 */
export declare interface LegacyMigration<Before = any, After = any> {
    up: (oldState: Before) => After;
    down: (newState: After) => Before;
}

/**
 * Legacy migration configuration with support for sub-type migrations.
 *
 * This interface extends the base legacy migration info to support migrations that
 * vary based on a sub-type key within records. This allows different migration paths
 * for different variants of the same record type, which was useful in older migration
 * systems but is now handled more elegantly by the current Migration system.
 * @public
 */
export declare interface LegacyMigrations extends LegacyBaseMigrationsInfo {
    subTypeKey?: string;
    subTypeMigrations?: Record<string, LegacyBaseMigrationsInfo>;
}

/**
 * Defines a single migration that transforms data from one schema version to another.
 *
 * A migration can operate at two different scopes:
 * - `record`: Transforms individual records, with optional filtering to target specific records
 * - `store`: Transforms the entire serialized store structure
 *
 * Each migration has a unique ID and can declare dependencies on other migrations that must
 * be applied first. The `up` function performs the forward transformation, while the optional
 * `down` function can reverse the migration if needed.
 * @public
 */
export declare 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;
} | {
    readonly down?: never;
    readonly scope: 'storage';
    readonly up: (storage: SynchronousRecordStorage<UnknownRecord>) => void;
});

/** @public */
export declare const MigrationFailureReason: {
    readonly IncompatibleSubtype: "incompatible-subtype";
    readonly MigrationError: "migration-error";
    readonly TargetVersionTooNew: "target-version-too-new";
    readonly TargetVersionTooOld: "target-version-too-old";
    readonly UnknownType: "unknown-type";
    readonly UnrecognizedSubtype: "unrecognized-subtype";
};

/** @public */
export declare type MigrationFailureReason = (typeof MigrationFailureReason)[keyof typeof MigrationFailureReason];

/** @public */
export declare namespace MigrationFailureReason {
    export type IncompatibleSubtype = typeof MigrationFailureReason.IncompatibleSubtype;
    export type UnknownType = typeof MigrationFailureReason.UnknownType;
    export type TargetVersionTooNew = typeof MigrationFailureReason.TargetVersionTooNew;
    export type TargetVersionTooOld = typeof MigrationFailureReason.TargetVersionTooOld;
    export type MigrationError = typeof MigrationFailureReason.MigrationError;
    export type UnrecognizedSubtype = typeof MigrationFailureReason.UnrecognizedSubtype;
}

/**
 * Unique identifier for a migration in the format `sequenceId/version`.
 *
 * Migration IDs follow a specific pattern where the sequence ID identifies the migration
 * sequence and the version number indicates the order within that sequence. For example:
 * 'com.myapp.book/1', 'com.myapp.book/2', etc.
 * @public
 */
export declare type MigrationId = `${string}/${number}`;

/**
 * Result type returned by migration operations.
 *
 * Migration operations can either succeed and return the transformed value,
 * or fail with a specific reason. This discriminated union type allows for
 * safe handling of both success and error cases when applying migrations.
 * @public
 */
export declare type MigrationResult<T> = {
    reason: MigrationFailureReason;
    type: 'error';
} | {
    type: 'success';
    value: T;
};

/**
 * A complete sequence of migrations that can be applied to transform data.
 *
 * A migration sequence represents a series of ordered migrations that belong together,
 * typically for a specific part of your schema. The sequence includes metadata about
 * whether it should be applied retroactively to existing data and contains the actual
 * migration definitions in execution order.
 * @public
 */
export declare interface MigrationSequence {
    sequenceId: string;
    /**
     * retroactive should be true if the migrations should be applied to snapshots that were created before
     * this migration sequence was added to the schema.
     *
     * In general:
     *
     * - retroactive should be true when app developers create their own new migration sequences.
     * - retroactive should be false when library developers ship a migration sequence. When you install a library for the first time, any migrations that were added in the library before that point should generally _not_ be applied to your existing data.
     */
    retroactive: boolean;
    sequence: Migration[];
}

/* Excluded from this release type: parseMigrationId */

/**
 * Query expression for filtering records by their property values. Maps record property names
 * to matching criteria.
 *
 * @example
 * ```ts
 * // Query for books published after 2020 that are in stock
 * const bookQuery: QueryExpression<Book> = {
 *   publishedYear: { gt: 2020 },
 *   inStock: { eq: true }
 * }
 *
 * // Query for books not by a specific author
 * const notByAuthor: QueryExpression<Book> = {
 *   authorId: { neq: 'author:tolkien' }
 * }
 *
 * // Query with nested properties
 * const nestedQuery: QueryExpression<Book> = {
 *   metadata: { sessionId: { eq: 'session:alpha' } }
 * }
 * ```
 *
 * @public
 */
/** @public */
export declare type QueryExpression<R extends object> = {
    [k in keyof R & string]?: R[k] extends boolean | null | number | string | undefined ? QueryValueMatcher<R[k]> : R[k] extends object ? QueryExpression<R[k]> : QueryValueMatcher<R[k]>;
};

/**
 * Defines matching criteria for query values. Supports equality, inequality, and greater-than comparisons.
 *
 * @example
 * ```ts
 * // Exact match
 * const exactMatch: QueryValueMatcher<string> = { eq: 'Science Fiction' }
 *
 * // Not equal to
 * const notMatch: QueryValueMatcher<string> = { neq: 'Romance' }
 *
 * // Greater than (numeric values only)
 * const greaterThan: QueryValueMatcher<number> = { gt: 2020 }
 * ```
 *
 * @public
 */
export declare type QueryValueMatcher<T> = {
    eq: T;
} | {
    gt: number;
} | {
    neq: T;
};

/**
 * Extracts the record type from a record ID type.
 *
 * @example
 * ```ts
 * type BookId = RecordId<Book>
 * type BookType = RecordFromId<BookId> // Book
 * ```
 *
 * @public
 */
export declare type RecordFromId<K extends RecordId<UnknownRecord>> = K extends RecordId<infer R> ? R : never;

/**
 * A branded string type that represents a unique identifier for a record.
 * The brand ensures type safety by preventing mixing of IDs between different record types.
 *
 * @example
 * ```ts
 * // Define a Book record
 * interface Book extends BaseRecord<'book', RecordId<Book>> {
 *   title: string
 *   author: string
 * }
 *
 * const bookId: RecordId<Book> = 'book:abc123' as RecordId<Book>
 * const authorId: RecordId<Author> = 'author:xyz789' as RecordId<Author>
 *
 * // TypeScript prevents mixing different record ID types
 * // bookId = authorId // Type error!
 * ```
 *
 * @public
 */
export declare type RecordId<R extends UnknownRecord> = string & {
    __type__: R;
};

/**
 * Defines the scope of the record
 *
 * session: The record belongs to a single instance of the store. It should not be synced, and any persistence logic should 'de-instance-ize' the record before persisting it, and apply the reverse when rehydrating.
 * document: The record is persisted and synced. It is available to all store instances.
 * presence: The record belongs to a single instance of the store. It may be synced to other instances, but other instances should not make changes to it. It should not be persisted.
 *
 * @public
 * */
export declare type RecordScope = 'document' | 'presence' | 'session';

/**
 * A diff describing the changes to records, containing collections of records that were added,
 * updated, or removed. This is the fundamental data structure used throughout the store system
 * to track and communicate changes.
 *
 * @example
 * ```ts
 * const diff: RecordsDiff<Book> = {
 *   added: {
 *     'book:1': { id: 'book:1', typeName: 'book', title: 'New Book' }
 *   },
 *   updated: {
 *     'book:2': [
 *       { id: 'book:2', typeName: 'book', title: 'Old Title' }, // from
 *       { id: 'book:2', typeName: 'book', title: 'New Title' }  // to
 *     ]
 *   },
 *   removed: {
 *     'book:3': { id: 'book:3', typeName: 'book', title: 'Deleted Book' }
 *   }
 * }
 * ```
 *
 * @public
 */
export declare interface RecordsDiff<R extends UnknownRecord> {
    /** Records that were created, keyed by their ID */
    added: Record<IdOf<R>, R>;
    /** Records that were modified, keyed by their ID. Each entry contains [from, to] tuple */
    updated: Record<IdOf<R>, [from: R, to: R]>;
    /** Records that were deleted, keyed by their ID */
    removed: Record<IdOf<R>, R>;
}

/**
 * A record type is a type that can be stored in a record store. It is created with
 * `createRecordType`.
 *
 * @public
 */
export declare class RecordType<R extends UnknownRecord, RequiredProperties extends keyof Omit<R, 'id' | 'typeName'>> {
    /**
     * The unique type associated with this record.
     *
     * @public
     * @readonly
     */
    readonly typeName: R['typeName'];
    /**
     * Factory function that creates default properties for new records.
     * @public
     */
    readonly createDefaultProperties: () => Exclude<Omit<R, 'id' | 'typeName'>, RequiredProperties>;
    /**
     * Validator function used to validate records of this type.
     * @public
     */
    readonly validator: StoreValidator<R>;
    /**
     * Optional configuration specifying which record properties are ephemeral.
     * Ephemeral properties are not included in snapshots or synchronization.
     * @public
     */
    readonly ephemeralKeys?: {
        readonly [K in Exclude<keyof R, 'id' | 'typeName'>]: boolean;
    };
    /**
     * Set of property names that are marked as ephemeral for efficient lookup.
     * @public
     */
    readonly ephemeralKeySet: ReadonlySet<string>;
    /**
     * The scope that determines how records of this type are persisted and synchronized.
     * @public
     */
    readonly scope: RecordScope;
    /**
     * Creates a new RecordType instance.
     *
     * typeName - The unique type name for records created by this RecordType
     * config - Configuration object for the RecordType
     *   - createDefaultProperties - Function that returns default properties for new records
     *   - validator - Optional validator function for record validation
     *   - scope - Optional scope determining persistence behavior (defaults to 'document')
     *   - ephemeralKeys - Optional mapping of property names to ephemeral status
     * @public
     */
    constructor(
    /**
     * The unique type associated with this record.
     *
     * @public
     * @readonly
     */
    typeName: R['typeName'], config: {
        readonly createDefaultProperties: () => Exclude<Omit<R, 'id' | 'typeName'>, RequiredProperties>;
        readonly ephemeralKeys?: {
            readonly [K in Exclude<keyof R, 'id' | 'typeName'>]: boolean;
        };
        readonly scope?: RecordScope;
        readonly validator?: StoreValidator<R>;
    });
    /**
     * Creates a new record of this type with the given properties.
     *
     * Properties are merged with default properties from the RecordType configuration.
     * If no id is provided, a unique id will be generated automatically.
     *
     * @example
     * ```ts
     * const book = Book.create({
     *   title: 'The Great Gatsby',
     *   author: 'F. Scott Fitzgerald'
     * })
     * // Result: { id: 'book:abc123', typeName: 'book', title: 'The Great Gatsby', author: 'F. Scott Fitzgerald', inStock: true }
     * ```
     *
     * @param properties - The properties for the new record, including both required and optional fields
     * @returns The newly created record with generated id and typeName
     * @public
     */
    create(properties: Expand<Pick<R, RequiredProperties> & Omit<Partial<R>, RequiredProperties>>): R;
    /**
     * Creates a deep copy of an existing record with a new unique id.
     *
     * This method performs a deep clone of all properties while generating a fresh id,
     * making it useful for duplicating records without id conflicts.
     *
     * @example
     * ```ts
     * const originalBook = Book.create({ title: '1984', author: 'George Orwell' })
     * const duplicatedBook = Book.clone(originalBook)
     * // duplicatedBook has same properties but different id
     * ```
     *
     * @param record - The record to clone
     * @returns A new record with the same properties but a different id
     * @public
     */
    clone(record: R): R;
    /**
     * Create a new ID for this record type.
     *
     * @example
     *
     * ```ts
     * const id = recordType.createId()
     * ```
     *
     * @returns The new ID.
     * @public
     */
    createId(customUniquePart?: string): IdOf<R>;
    /**
     * Extracts the unique identifier part from a full record id.
     *
     * Record ids have the format `typeName:uniquePart`. This method returns just the unique part.
     *
     * @example
     * ```ts
     * const bookId = Book.createId() // 'book:abc123'
     * const uniquePart = Book.parseId(bookId) // 'abc123'
     * ```
     *
     * @param id - The full record id to parse
     * @returns The unique identifier portion after the colon
     * @throws Error if the id is not valid for this record type
     * @public
     */
    parseId(id: IdOf<R>): string;
    /**
     * Type guard that checks whether a record belongs to this RecordType.
     *
     * This method performs a runtime check by comparing the record's typeName
     * against this RecordType's typeName.
     *
     * @example
     * ```ts
     * if (Book.isInstance(someRecord)) {
     *   // someRecord is now typed as a book record
     *   console.log(someRecord.title)
     * }
     * ```
     *
     * @param record - The record to check, may be undefined
     * @returns True if the record is an instance of this record type
     * @public
     */
    isInstance(record?: UnknownRecord): record is R;
    /**
     * Type guard that checks whether an id string belongs to this RecordType.
     *
     * Validates that the id starts with this RecordType's typeName followed by a colon.
     * This is more efficient than parsing the full id when you only need to verify the type.
     *
     * @example
     * ```ts
     * if (Book.isId(someId)) {
     *   // someId is now typed as IdOf<BookRecord>
     *   const book = store.get(someId)
     * }
     * ```
     *
     * @param id - The id string to check, may be undefined
     * @returns True if the id belongs to this record type
     * @public
     */
    isId(id?: string): id is IdOf<R>;
    /**
     * Create a new RecordType that has the same type name as this RecordType and includes the given
     * default properties.
     *
     * @example
     *
     * ```ts
     * const authorType = createRecordType('author', () => ({ living: true }))
     * const deadAuthorType = authorType.withDefaultProperties({ living: false })
     * ```
     *
     * @param createDefaultProperties - A function that returns the default properties of the new RecordType.
     * @returns The new RecordType.
     */
    withDefaultProperties<DefaultProps extends Omit<Partial<R>, 'id' | 'typeName'>>(createDefaultProperties: () => DefaultProps): RecordType<R, Exclude<RequiredProperties, keyof DefaultProps>>;
    /**
     * Validates a record against this RecordType's validator and returns it with proper typing.
     *
     * This method runs the configured validator function and throws an error if validation fails.
     * If a previous version of the record is provided, it may use optimized validation.
     *
     * @example
     * ```ts
     * try {
     *   const validBook = Book.validate(untrustedData)
     *   // validBook is now properly typed and validated
     * } catch (error) {
     *   console.log('Validation failed:', error.message)
     * }
     * ```
     *
     * @param record - The unknown record data to validate
     * @param recordBefore - Optional previous version for optimized validation
     * @returns The validated and properly typed record
     * @throws Error if validation fails
     * @public
     */
    validate(record: unknown, recordBefore?: R): R;
}

/**
 * Creates the inverse of a RecordsDiff, effectively reversing all changes.
 * Added records become removed, removed records become added, and updated records
 * have their from/to values swapped. This is useful for implementing undo operations.
 *
 * @param diff - The diff to reverse
 * @returns A new RecordsDiff that represents the inverse of the input diff
 * @example
 * ```ts
 * const originalDiff: RecordsDiff<Book> = {
 *   added: { 'book:1': newBook },
 *   updated: { 'book:2': [oldBook, updatedBook] },
 *   removed: { 'book:3': deletedBook }
 * }
 *
 * const reversedDiff = reverseRecordsDiff(originalDiff)
 * // Result: {
 * //   added: { 'book:3': deletedBook },
 * //   updated: { 'book:2': [updatedBook, oldBook] },
 * //   removed: { 'book:1': newBook }
 * // }
 * ```
 *
 * @public
 */
export declare function reverseRecordsDiff(diff: RecordsDiff<any>): RecordsDiff<any>;

/**
 * A reactive computed index that provides efficient lookups of records by property values.
 * Returns a computed value containing an RSIndexMap with diffs for change tracking.
 *
 * @example
 * ```ts
 * // Create an index on book authors
 * const authorIndex: RSIndex<Book, 'authorId'> = store.query.index('book', 'authorId')
 *
 * // Get all books by a specific author
 * const leguinBooks = authorIndex.get().get('author:leguin')
 * ```
 *
 * @public
 */
export declare type RSIndex<R extends UnknownRecord> = Computed<RSIndexMap<R>, RSIndexDiff<R>>;

/**
 * A type representing the diff of changes to a reactive store index.
 * Maps property values to the collection differences for record IDs that have that property value.
 *
 * @example
 * ```ts
 * // For an index on book titles, the diff might look like:
 * const titleIndexDiff: RSIndexDiff<Book, 'title'> = new Map([
 *   ['The Lathe of Heaven', { added: new Set(['book:1']), removed: new Set() }],
 *   ['Animal Farm', { added: new Set(), removed: new Set(['book:2']) }]
 * ])
 * ```
 *
 * @public
 */
export declare type RSIndexDiff<R extends UnknownRecord> = Map<any, CollectionDiff<IdOf<R>>>;

/**
 * A type representing a reactive store index as a map from property values to sets of record IDs.
 * This is used to efficiently look up records by a specific property value.
 *
 * @example
 * ```ts
 * // Index mapping book titles to the IDs of books with that title
 * const titleIndex: RSIndexMap<Book, 'title'> = new Map([
 *   ['The Lathe of Heaven', new Set(['book:1'])],
 *   ['Animal Farm', new Set(['book:2', 'book:3'])]
 * ])
 * ```
 *
 * @public
 */
export declare type RSIndexMap<R extends UnknownRecord> = Map<any, Set<IdOf<R>>>;

/**
 * Union type representing all supported serialized schema formats.
 *
 * This type allows the store to handle both legacy (V1) and current (V2)
 * schema formats during deserialization and migration.
 *
 * @example
 * ```ts
 * function handleSchema(schema: SerializedSchema) {
 *   if (schema.schemaVersion === 1) {
 *     // Handle V1 format
 *     console.log('Store version:', schema.storeVersion)
 *   } else {
 *     // Handle V2 format
 *     console.log('Sequences:', schema.sequences)
 *   }
 * }
 * ```
 *
 * @public
 */
export declare type SerializedSchema = SerializedSchemaV1 | SerializedSchemaV2;

/**
 * Version 1 format for serialized store schema information.
 *
 * This is the legacy format used before schema version 2. Version 1 schemas
 * separate store-level versioning from record-level versioning, and support
 * subtypes for complex record types like shapes.
 *
 * @example
 * ```ts
 * const schemaV1: SerializedSchemaV1 = {
 *   schemaVersion: 1,
 *   storeVersion: 2,
 *   recordVersions: {
 *     book: { version: 3 },
 *     shape: {
 *       version: 2,
 *       subTypeVersions: { rectangle: 1, circle: 2 },
 *       subTypeKey: 'type'
 *     }
 *   }
 * }
 * ```
 *
 * @public
 */
export declare interface SerializedSchemaV1 {
    /** Schema version is the version for this type you're looking at right now */
    schemaVersion: 1;
    /**
     * Store version is the version for the structure of the store. e.g. higher level structure like
     * removing or renaming a record type.
     */
    storeVersion: number;
    /** Record versions are the versions for each record type. e.g. adding a new field to a record */
    recordVersions: Record<string, {
        subTypeKey: string;
        subTypeVersions: Record<string, number>;
        version: number;
    } | {
        version: number;
    }>;
}

/**
 * Version 2 format for serialized store schema information.
 *
 * This is the current format that uses a unified sequence-based approach
 * for tracking versions across all migration sequences. Each sequence ID
 * maps to the latest version number for that sequence.
 *
 * @example
 * ```ts
 * const schemaV2: SerializedSchemaV2 = {
 *   schemaVersion: 2,
 *   sequences: {
 *     'com.tldraw.store': 3,
 *     'com.tldraw.book': 2,
 *     'com.tldraw.shape': 4,
 *     'com.tldraw.shape.rectangle': 1
 *   }
 * }
 * ```
 *
 * @public
 */
export declare interface SerializedSchemaV2 {
    schemaVersion: 2;
    sequences: {
        [sequenceId: string]: number;
    };
}

/**
 * A serialized snapshot of the record store's values.
 * This is a plain JavaScript object that can be saved to storage or transmitted over the network.
 *
 * @example
 * ```ts
 * const serialized: SerializedStore<Book> = {
 *   'book:123': { id: 'book:123', typeName: 'book', title: 'The Lathe of Heaven' },
 *   'book:456': { id: 'book:456', typeName: 'book', title: 'The Left Hand of Darkness' }
 * }
 * ```
 *
 * @public
 */
export declare type SerializedStore<R extends UnknownRecord> = Record<IdOf<R>, R>;

/**
 * Combines multiple RecordsDiff objects into a single consolidated diff.
 * This function intelligently merges changes, handling cases where the same record
 * is modified multiple times across different diffs. For example, if a record is
 * added in one diff and then updated in another, the result will show it as added
 * with the final state.
 *
 * @param diffs - An array of diffs to combine into a single diff
 * @param options - Configuration options for the squashing operation
 *   - mutateFirstDiff - If true, modifies the first diff in place instead of creating a new one
 * @returns A single diff that represents the cumulative effect of all input diffs
 * @example
 * ```ts
 * const diff1: RecordsDiff<Book> = {
 *   added: { 'book:1': { id: 'book:1', title: 'New Book' } },
 *   updated: {},
 *   removed: {}
 * }
 *
 * const diff2: RecordsDiff<Book> = {
 *   added: {},
 *   updated: { 'book:1': [{ id: 'book:1', title: 'New Book' }, { id: 'book:1', title: 'Updated Title' }] },
 *   removed: {}
 * }
 *
 * const squashed = squashRecordDiffs([diff1, diff2])
 * // Result: {
 * //   added: { 'book:1': { id: 'book:1', title: 'Updated Title' } },
 * //   updated: {},
 * //   removed: {}
 * // }
 * ```
 *
 * @public
 */
export declare function squashRecordDiffs<T extends UnknownRecord>(diffs: RecordsDiff<T>[], options?: {
    mutateFirstDiff?: boolean;
}): RecordsDiff<T>;

/* Excluded from this release type: squashRecordDiffsMutable */

/**
 * Declares dependencies for migrations without being a migration itself.
 *
 * This interface allows you to specify that future migrations in a sequence depend on
 * migrations from other sequences, without defining an actual migration transformation.
 * It's used to establish cross-sequence dependencies in the migration graph.
 * @public
 */
export declare interface StandaloneDependsOn {
    readonly dependsOn: readonly MigrationId[];
}

/**
 * A reactive store that manages collections of typed records.
 *
 * The Store is the central container for your application's data, providing:
 * - Reactive state management with automatic updates
 * - Type-safe record operations
 * - History tracking and change notifications
 * - Schema validation and migrations
 * - Side effects and business logic hooks
 * - Efficient querying and indexing
 *
 * @example
 * ```ts
 * // Create a store with schema
 * const schema = StoreSchema.create({
 *   book: Book,
 *   author: Author
 * })
 *
 * const store = new Store({
 *   schema,
 *   props: {}
 * })
 *
 * // Add records
 * const book = Book.create({ title: 'The Lathe of Heaven', author: 'Le Guin' })
 * store.put([book])
 *
 * // Listen to changes
 * store.listen((entry) => {
 *   console.log('Changes:', entry.changes)
 * })
 * ```
 *
 * @public
 */
export declare class Store<R extends UnknownRecord = UnknownRecord, Props = unknown> {
    /**
     * The unique identifier of the store instance.
     *
     * @public
     */
    readonly id: string;
    /* Excluded from this release type: records */
    /**
     * An atom containing the store's history.
     *
     * @public
     * @readonly
     */
    readonly history: Atom<number, RecordsDiff<R>>;
    /**
     * Reactive queries and indexes for efficiently accessing store data.
     * Provides methods for filtering, indexing, and subscribing to subsets of records.
     *
     * @example
     * ```ts
     * // Create an index by a property
     * const booksByAuthor = store.query.index('book', 'author')
     *
     * // Get records matching criteria
     * const inStockBooks = store.query.records('book', () => ({
     *   inStock: { eq: true }
     * }))
     * ```
     *
     * @public
     * @readonly
     */
    readonly query: StoreQueries<R>;
    /* Excluded from this release type: listeners */
    /* Excluded from this release type: historyAccumulator */
    /* Excluded from this release type: historyReactor */
    /* Excluded from this release type: cancelHistoryReactor */
    /**
     * The schema that defines the structure and validation rules for records in this store.
     *
     * @public
     */
    readonly schema: StoreSchema<R, Props>;
    /**
     * Custom properties associated with this store instance.
     *
     * @public
     */
    readonly props: Props;
    /**
     * A mapping of record scopes to the set of record type names that belong to each scope.
     * Used to filter records by their persistence and synchronization behavior.
     *
     * @public
     */
    readonly scopedTypes: {
        readonly [K in RecordScope]: ReadonlySet<R['typeName']>;
    };
    /**
     * Side effects manager that handles lifecycle events for record operations.
     * Allows registration of callbacks for create, update, delete, and validation events.
     *
     * @example
     * ```ts
     * store.sideEffects.registerAfterCreateHandler('book', (book) => {
     *   console.log('Book created:', book.title)
     * })
     * ```
     *
     * @public
     */
    readonly sideEffects: StoreSideEffects<R>;
    /**
     * Creates a new Store instance.
     *
     * @example
     * ```ts
     * const store = new Store({
     *   schema: StoreSchema.create({ book: Book }),
     *   props: { appName: 'MyLibrary' },
     *   initialData: savedData
     * })
     * ```
     *
     * @param config - Configuration object for the store
     */
    constructor(config: {
        /** Custom properties for the store instance */
        props: Props;
        /** Optional unique identifier for the store */
        id?: string;
        /** The schema defining record types, validation, and migrations */
        schema: StoreSchema<R, Props>;
        /** The store's initial data to populate on creation */
        initialData?: SerializedStore<R>;
    });
    _flushHistory(): void;
    dispose(): void;
    /**
     * Filters out non-document changes from a diff. Returns null if there are no changes left.
     * @param change - the records diff
     * @param scope - the records scope
     * @returns
     */
    filterChangesByScope(change: RecordsDiff<R>, scope: RecordScope): {
        added: { [K in IdOf<R>]: R; };
        removed: { [K in IdOf<R>]: R; };
        updated: { [K in IdOf<R>]: [from: R, to: R]; };
    } | null;
    /**
     * Update the history with a diff of changes.
     *
     * @param changes - The changes to add to the history.
     */
    private updateHistory;
    validate(phase: 'createRecord' | 'initialize' | 'tests' | 'updateRecord'): void;
    /**
     * Add or update records in the store. If a record with the same ID already exists, it will be updated.
     * Otherwise, a new record will be created.
     *
     * @example
     * ```ts
     * // Add new records
     * const book = Book.create({ title: 'Lathe Of Heaven', author: 'Le Guin' })
     * store.put([book])
     *
     * // Update existing record
     * store.put([{ ...book, title: 'The Lathe of Heaven' }])
     * ```
     *
     * @param records - The records to add or update
     * @param phaseOverride - Override the validation phase (used internally)
     * @public
     */
    put(records: R[], phaseOverride?: 'initialize'): void;
    /**
     * Remove records from the store by their IDs.
     *
     * @example
     * ```ts
     * // Remove a single record
     * store.remove([book.id])
     *
     * // Remove multiple records
     * store.remove([book1.id, book2.id, book3.id])
     * ```
     *
     * @param ids - The IDs of the records to remove
     * @public
     */
    remove(ids: IdOf<R>[]): void;
    /**
     * Get a record by its ID. This creates a reactive subscription to the record.
     *
     * @example
     * ```ts
     * const book = store.get(bookId)
     * if (book) {
     *   console.log(book.title)
     * }
     * ```
     *
     * @param id - The ID of the record to get
     * @returns The record if it exists, undefined otherwise
     * @public
     */
    get<K extends IdOf<R>>(id: K): RecordFromId<K> | undefined;
    /**
     * Get a record by its ID without creating a reactive subscription.
     * Use this when you need to access a record but don't want reactive updates.
     *
     * @example
     * ```ts
     * // Won't trigger reactive updates when this record changes
     * const book = store.unsafeGetWithoutCapture(bookId)
     * ```
     *
     * @param id - The ID of the record to get
     * @returns The record if it exists, undefined otherwise
     * @public
     */
    unsafeGetWithoutCapture<K extends IdOf<R>>(id: K): RecordFromId<K> | undefined;
    /**
     * Serialize the store's records to a plain JavaScript object.
     * Only includes records matching the specified scope.
     *
     * @example
     * ```ts
     * // Serialize only document records (default)
     * const documentData = store.serialize('document')
     *
     * // Serialize all records
     * const allData = store.serialize('all')
     * ```
     *
     * @param scope - The scope of records to serialize. Defaults to 'document'
     * @returns The serialized store data
     * @public
     */
    serialize(scope?: 'all' | RecordScope): SerializedStore<R>;
    /**
     * Get a serialized snapshot of the store and its schema.
     * This includes both the data and schema information needed for proper migration.
     *
     * @example
     * ```ts
     * const snapshot = store.getStoreSnapshot()
     * localStorage.setItem('myApp', JSON.stringify(snapshot))
     *
     * // Later...
     * const saved = JSON.parse(localStorage.getItem('myApp'))
     * store.loadStoreSnapshot(saved)
     * ```
     *
     * @param scope - The scope of records to serialize. Defaults to 'document'
     * @returns A snapshot containing both store data and schema information
     * @public
     */
    getStoreSnapshot(scope?: 'all' | RecordScope): StoreSnapshot<R>;
    /**
     * Migrate a serialized snapshot to the current schema version.
     * This applies any necessary migrations to bring old data up to date.
     *
     * @example
     * ```ts
     * const oldSnapshot = JSON.parse(localStorage.getItem('myApp'))
     * const migratedSnapshot = store.migrateSnapshot(oldSnapshot)
     * ```
     *
     * @param snapshot - The snapshot to migrate
     * @returns The migrated snapshot with current schema version
     * @throws Error if migration fails
     * @public
     */
    migrateSnapshot(snapshot: StoreSnapshot<R>): StoreSnapshot<R>;
    /**
     * Load a serialized snapshot into the store, replacing all current data.
     * The snapshot will be automatically migrated to the current schema version if needed.
     *
     * @example
     * ```ts
     * const snapshot = JSON.parse(localStorage.getItem('myApp'))
     * store.loadStoreSnapshot(snapshot)
     * ```
     *
     * @param snapshot - The snapshot to load
     * @throws Error if migration fails or snapshot is invalid
     * @public
     */
    loadStoreSnapshot(snapshot: StoreSnapshot<R>): void;
    /**
     * Get an array of all records in the store.
     *
     * @example
     * ```ts
     * const allRecords = store.allRecords()
     * const books = allRecords.filter(r => r.typeName === 'book')
     * ```
     *
     * @returns An array containing all records in the store
     * @public
     */
    allRecords(): R[];
    /**
     * Remove all records from the store.
     *
     * @example
     * ```ts
     * store.clear()
     * console.log(store.allRecords().length) // 0
     * ```
     *
     * @public
     */
    clear(): void;
    /**
     * Update a single record using an updater function. To update multiple records at once,
     * use the `update` method of the `TypedStore` class.
     *
     * @example
     * ```ts
     * store.update(book.id, (book) => ({
     *   ...book,
     *   title: 'Updated Title'
     * }))
     * ```
     *
     * @param id - The ID of the record to update
     * @param updater - A function that receives the current record and returns the updated record
     * @public
     */
    update<K extends IdOf<R>>(id: K, updater: (record: RecordFromId<K>) => RecordFromId<K>): void;
    /**
     * Check whether a record with the given ID exists in the store.
     *
     * @example
     * ```ts
     * if (store.has(bookId)) {
     *   console.log('Book exists!')
     * }
     * ```
     *
     * @param id - The ID of the record to check
     * @returns True if the record exists, false otherwise
     * @public
     */
    has<K extends IdOf<R>>(id: K): boolean;
    /**
     * Add a listener that will be called when the store changes.
     * Returns a function to remove the listener.
     *
     * @example
     * ```ts
     * const removeListener = store.listen((entry) => {
     *   console.log('Changes:', entry.changes)
     *   console.log('Source:', entry.source)
     * })
     *
     * // Listen only to user changes to document records
     * const removeDocumentListener = store.listen(
     *   (entry) => console.log('Document changed:', entry),
     *   { source: 'user', scope: 'document' }
     * )
     *
     * // Later, remove the listener
     * removeListener()
     * ```
     *
     * @param onHistory - The listener function to call when changes occur
     * @param filters - Optional filters to control when the listener is called
     * @returns A function that removes the listener when called
     * @public
     */
    listen(onHistory: StoreListener<R>, filters?: Partial<StoreListenerFilters>): () => void;
    private isMergingRemoteChanges;
    /**
     * Merge changes from a remote source. Changes made within the provided function
     * will be marked with source 'remote' instead of 'user'.
     *
     * @example
     * ```ts
     * // Changes from sync/collaboration
     * store.mergeRemoteChanges(() => {
     *   store.put(remoteRecords)
     *   store.remove(deletedIds)
     * })
     * ```
     *
     * @param fn - A function that applies the remote changes
     * @public
     */
    mergeRemoteChanges(fn: () => void): void;
    /**
     * Run `fn` and return a {@link RecordsDiff} of the changes that occurred as a result.
     */
    extractingChanges(fn: () => void): RecordsDiff<R>;
    applyDiff(diff: RecordsDiff<R>, { runCallbacks, ignoreEphemeralKeys }?: {
        ignoreEphemeralKeys?: boolean;
        runCallbacks?: boolean;
    }): void;
    /**
     * Create a cache based on values in the store. Pass in a function that takes and ID and a
     * signal for the underlying record. Return a signal (usually a computed) for the cached value.
     * For simple derivations, use {@link Store.createComputedCache}. This function is useful if you
     * need more precise control over intermediate values.
     */
    createCache<Result, Record extends R = R>(create: (id: IdOf<Record>, recordSignal: Signal<R>) => Signal<Result>): {
        get: (id: IdOf<Record>) => Result | undefined;
    };
    /**
     * Create a computed cache.
     *
     * @param name - The name of the derivation cache.
     * @param derive - A function used to derive the value of the cache.
     * @param opts - Options for the computed cache.
     * @public
     */
    createComputedCache<Result, Record extends R = R>(name: string, derive: (record: Record) => Result | undefined, opts?: CreateComputedCacheOpts<Result, Record>): ComputedCache<Result, Record>;
    private _integrityChecker?;
    /* Excluded from this release type: ensureStoreIsUsable */
    private _isPossiblyCorrupted;
    /* Excluded from this release type: markAsPossiblyCorrupted */
    /* Excluded from this release type: isPossiblyCorrupted */
    private pendingAfterEvents;
    private addDiffForAfterEvent;
    private flushAtomicCallbacks;
    private _isInAtomicOp;
    /* Excluded from this release type: atomic */
    /* Excluded from this release type: addHistoryInterceptor */
}

/**
 * Handler function called after a record has been successfully updated in the store.
 * Use this for side effects that should happen after record changes, such as
 * updating related records or maintaining consistency constraints.
 *
 * @param prev - The previous version of the record
 * @param next - The new version of the record that was stored
 * @param source - Whether the change originated from 'user' interaction or 'remote' synchronization
 *
 * @example
 * ```ts
 * const handler: StoreAfterChangeHandler<ShapeRecord> = (prev, next, source) => {
 *   // Update connected arrows when a shape moves
 *   if (prev.x !== next.x || prev.y !== next.y) {
 *     updateConnectedArrows(next.id)
 *   }
 * }
 * ```
 *
 * @public
 */
export declare type StoreAfterChangeHandler<R extends UnknownRecord> = (prev: R, next: R, source: 'remote' | 'user') => void;

/**
 * Handler function called after a record has been successfully created in the store.
 * Use this for side effects that should happen after record creation, such as updating
 * related records or triggering notifications.
 *
 * @param record - The record that was created
 * @param source - Whether the change originated from 'user' interaction or 'remote' synchronization
 *
 * @example
 * ```ts
 * const handler: StoreAfterCreateHandler<BookRecord> = (book, source) => {
 *   if (source === 'user') {
 *     console.log(`New book added: ${book.title}`)
 *     updateAuthorBookCount(book.authorId)
 *   }
 * }
 * ```
 *
 * @public
 */
export declare type StoreAfterCreateHandler<R extends UnknownRecord> = (record: R, source: 'remote' | 'user') => void;

/**
 * Handler function called after a record has been successfully deleted from the store.
 * Use this for cleanup operations and maintaining referential integrity.
 *
 * @param record - The record that was deleted
 * @param source - Whether the change originated from 'user' interaction or 'remote' synchronization
 *
 * @example
 * ```ts
 * const handler: StoreAfterDeleteHandler<ShapeRecord> = (shape, source) => {
 *   // Clean up arrows that were connected to this shape
 *   const connectedArrows = findArrowsConnectedTo(shape.id)
 *   store.remove(connectedArrows.map(arrow => arrow.id))
 * }
 * ```
 *
 * @public
 */
export declare type StoreAfterDeleteHandler<R extends UnknownRecord> = (record: R, source: 'remote' | 'user') => void;

/**
 * Handler function called before a record is updated in the store.
 * The handler receives the current and new versions of the record and can return
 * a modified version or the original to prevent the change.
 *
 * @param prev - The current version of the record in the store
 * @param next - The proposed new version of the record
 * @param source - Whether the change originated from 'user' interaction or 'remote' synchronization
 * @returns The record version to actually store (may be modified or the original to block change)
 *
 * @example
 * ```ts
 * const handler: StoreBeforeChangeHandler<ShapeRecord> = (prev, next, source) => {
 *   // Prevent shapes from being moved outside the canvas bounds
 *   if (next.x < 0 || next.y < 0) {
 *     return prev // Block the change
 *   }
 *   return next
 * }
 * ```
 *
 * @public
 */
export declare type StoreBeforeChangeHandler<R extends UnknownRecord> = (prev: R, next: R, source: 'remote' | 'user') => R;

/**
 * Handler function called before a record is created in the store.
 * The handler receives the record to be created and can return a modified version.
 * Use this to validate, transform, or modify records before they are added to the store.
 *
 * @param record - The record about to be created
 * @param source - Whether the change originated from 'user' interaction or 'remote' synchronization
 * @returns The record to actually create (may be modified)
 *
 * @example
 * ```ts
 * const handler: StoreBeforeCreateHandler<MyRecord> = (record, source) => {
 *   // Ensure all user-created records have a timestamp
 *   if (source === 'user' && !record.createdAt) {
 *     return { ...record, createdAt: Date.now() }
 *   }
 *   return record
 * }
 * ```
 *
 * @public
 */
export declare type StoreBeforeCreateHandler<R extends UnknownRecord> = (record: R, source: 'remote' | 'user') => R;

/**
 * Handler function called before a record is deleted from the store.
 * The handler can return `false` to prevent the deletion from occurring.
 *
 * @param record - The record about to be deleted
 * @param source - Whether the change originated from 'user' interaction or 'remote' synchronization
 * @returns `false` to prevent deletion, `void` or any other value to allow it
 *
 * @example
 * ```ts
 * const handler: StoreBeforeDeleteHandler<BookRecord> = (book, source) => {
 *   // Prevent deletion of books that are currently checked out
 *   if (book.isCheckedOut) {
 *     console.warn('Cannot delete checked out book')
 *     return false
 *   }
 *   // Allow deletion for other books
 * }
 * ```
 *
 * @public
 */
export declare type StoreBeforeDeleteHandler<R extends UnknownRecord> = (record: R, source: 'remote' | 'user') => false | void;

/**
 * Information about an error that occurred in the store.
 *
 * @example
 * ```ts
 * const error: StoreError = {
 *   error: new Error('Validation failed'),
 *   phase: 'updateRecord',
 *   recordBefore: oldRecord,
 *   recordAfter: newRecord,
 *   isExistingValidationIssue: false
 * }
 * ```
 *
 * @public
 */
export declare interface StoreError {
    /** The error that occurred */
    error: Error;
    /** The phase during which the error occurred */
    phase: 'createRecord' | 'initialize' | 'tests' | 'updateRecord';
    /** The record state before the operation (if applicable) */
    recordBefore?: unknown;
    /** The record state after the operation */
    recordAfter: unknown;
    /** Whether this is an existing validation issue */
    isExistingValidationIssue: boolean;
}

/**
 * A function that will be called when the history changes.
 *
 * @example
 * ```ts
 * const listener: StoreListener<Book> = (entry) => {
 *   console.log('Changes:', entry.changes)
 *   console.log('Source:', entry.source)
 * }
 *
 * store.listen(listener)
 * ```
 *
 * @param entry - The history entry containing the changes
 *
 * @public
 */
export declare type StoreListener<R extends UnknownRecord> = (entry: HistoryEntry<R>) => void;

/**
 * Filters for store listeners to control which changes trigger the listener.
 *
 * @example
 * ```ts
 * const filters: StoreListenerFilters = {
 *   source: 'user', // Only listen to user changes
 *   scope: 'document' // Only listen to document-scoped records
 * }
 * ```
 *
 * @public
 */
export declare interface StoreListenerFilters {
    /** Filter by the source of changes */
    source: 'all' | ChangeSource;
    /** Filter by the scope of records */
    scope: 'all' | RecordScope;
}

/**
 * A store or an object containing a store.
 * This type is used for APIs that can accept either a store directly or an object with a store property.
 *
 * @example
 * ```ts
 * function useStore(storeOrObject: StoreObject<MyRecord>) {
 *   const store = storeOrObject instanceof Store ? storeOrObject : storeOrObject.store
 *   return store
 * }
 * ```
 *
 * @public
 */
export declare type StoreObject<R extends UnknownRecord> = {
    store: Store<R>;
} | Store<R>;

/**
 * Extract the record type from a StoreObject.
 *
 * @example
 * ```ts
 * type MyStoreObject = { store: Store<Book | Author> }
 * type Records = StoreObjectRecordType<MyStoreObject> // Book | Author
 * ```
 *
 * @public
 */
export declare type StoreObjectRecordType<Context extends StoreObject<any>> = Context extends Store<infer R> ? R : Context extends {
    store: Store<infer R>;
} ? R : never;

/**
 * Handler function called when a store operation (atomic transaction) completes.
 * This is useful for performing actions after a batch of changes has been applied,
 * such as triggering saves or sending notifications.
 *
 * @param source - Whether the operation originated from 'user' interaction or 'remote' synchronization
 *
 * @example
 * ```ts
 * const handler: StoreOperationCompleteHandler = (source) => {
 *   if (source === 'user') {
 *     // Auto-save after user operations complete
 *     saveStoreSnapshot()
 *   }
 * }
 * ```
 *
 * @public
 */
export declare type StoreOperationCompleteHandler = (source: 'remote' | 'user') => void;

/**
 * A class that provides reactive querying capabilities for a record store.
 * Offers methods to create indexes, filter records, and perform efficient lookups with automatic cache management.
 * All queries are reactive and will automatically update when the underlying store data changes.
 *
 * @example
 * ```ts
 * // Create a store with books
 * const store = new Store({ schema: StoreSchema.create({ book: Book, author: Author }) })
 *
 * // Get reactive queries for books
 * const booksByAuthor = store.query.index('book', 'authorId')
 * const inStockBooks = store.query.records('book', () => ({ inStock: { eq: true } }))
 * ```
 *
 * @public
 */
export declare class StoreQueries<R extends UnknownRecord> {
    private readonly recordMap;
    private readonly history;
    /* Excluded from this release type: __constructor */
    /* Excluded from this release type: indexCache */
    /* Excluded from this release type: historyCache */
    /* Excluded from this release type: getAllIdsForType */
    /* Excluded from this release type: getRecordById */
    /* Excluded from this release type: getNestedValue */
    /**
     * Creates a reactive computed that tracks the change history for records of a specific type.
     * The returned computed provides incremental diffs showing what records of the given type
     * have been added, updated, or removed.
     *
     * @param typeName - The type name to filter the history by
     * @returns A computed value containing the current epoch and diffs of changes for the specified type
     *
     * @example
     * ```ts
     * // Track changes to book records only
     * const bookHistory = store.query.filterHistory('book')
     *
     * // React to book changes
     * react('book-changes', () => {
     *   const currentEpoch = bookHistory.get()
     *   console.log('Books updated at epoch:', currentEpoch)
     * })
     * ```
     *
     * @public
     */
    filterHistory<TypeName extends R['typeName']>(typeName: TypeName): Computed<number, RecordsDiff<Extract<R, {
        typeName: TypeName;
    }>>>;
    /**
     * Creates a reactive index that maps property values to sets of record IDs for efficient lookups.
     * The index automatically updates when records are added, updated, or removed, and results are cached
     * for performance.
     *
     * Supports nested property paths using backslash separator (e.g., 'metadata\\sessionId').
     *
     * @param typeName - The type name of records to index
     * @param path - The property name or backslash-delimited path to index by
     * @returns A reactive computed containing the index map with change diffs
     *
     * @example
     * ```ts
     * // Create an index of books by author ID
     * const booksByAuthor = store.query.index('book', 'authorId')
     *
     * // Get all books by a specific author
     * const authorBooks = booksByAuthor.get().get('author:leguin')
     * console.log(authorBooks) // Set<RecordId<Book>>
     *
     * // Index by nested property using backslash separator
     * const booksBySession = store.query.index('book', 'metadata\\sessionId')
     * const sessionBooks = booksBySession.get().get('session:alpha')
     * ```
     *
     * @public
     */
    index<TypeName extends R['typeName']>(typeName: TypeName, path: string): RSIndex<Extract<R, {
        typeName: TypeName;
    }>>;
    /* Excluded from this release type: __uncached_createIndex */
    /**
     * Creates a reactive query that returns the first record matching the given query criteria.
     * Returns undefined if no matching record is found. The query automatically updates
     * when records change.
     *
     * @param typeName - The type name of records to query
     * @param queryCreator - Function that returns the query expression object to match against
     * @param name - Optional name for the query computation (used for debugging)
     * @returns A computed value containing the first matching record or undefined
     *
     * @example
     * ```ts
     * // Find the first book with a specific title
     * const bookLatheOfHeaven = store.query.record('book', () => ({ title: { eq: 'The Lathe of Heaven' } }))
     * console.log(bookLatheOfHeaven.get()?.title) // 'The Lathe of Heaven' or undefined
     *
     * // Find any book in stock
     * const anyInStockBook = store.query.record('book', () => ({ inStock: { eq: true } }))
     * ```
     *
     * @public
     */
    record<TypeName extends R['typeName']>(typeName: TypeName, queryCreator?: () => QueryExpression<Extract<R, {
        typeName: TypeName;
    }>>, name?: string): Computed<Extract<R, {
        typeName: TypeName;
    }> | undefined>;
    /**
     * Creates a reactive query that returns an array of all records matching the given query criteria.
     * The array automatically updates when records are added, updated, or removed.
     *
     * @param typeName - The type name of records to query
     * @param queryCreator - Function that returns the query expression object to match against
     * @param name - Optional name for the query computation (used for debugging)
     * @returns A computed value containing an array of all matching records
     *
     * @example
     * ```ts
     * // Get all books in stock
     * const inStockBooks = store.query.records('book', () => ({ inStock: { eq: true } }))
     * console.log(inStockBooks.get()) // Book[]
     *
     * // Get all books by a specific author
     * const leguinBooks = store.query.records('book', () => ({ authorId: { eq: 'author:leguin' } }))
     *
     * // Get all books (no filter)
     * const allBooks = store.query.records('book')
     * ```
     *
     * @public
     */
    records<TypeName extends R['typeName']>(typeName: TypeName, queryCreator?: () => QueryExpression<Extract<R, {
        typeName: TypeName;
    }>>, name?: string): Computed<Array<Extract<R, {
        typeName: TypeName;
    }>>>;
    /**
     * Creates a reactive query that returns a set of record IDs matching the given query criteria.
     * This is more efficient than `records()` when you only need the IDs and not the full record objects.
     * The set automatically updates with collection diffs when records change.
     *
     * @param typeName - The type name of records to query
     * @param queryCreator - Function that returns the query expression object to match against
     * @param name - Optional name for the query computation (used for debugging)
     * @returns A computed value containing a set of matching record IDs with collection diffs
     *
     * @example
     * ```ts
     * // Get IDs of all books in stock
     * const inStockBookIds = store.query.ids('book', () => ({ inStock: { eq: true } }))
     * console.log(inStockBookIds.get()) // Set<RecordId<Book>>
     *
     * // Get all book IDs (no filter)
     * const allBookIds = store.query.ids('book')
     *
     * // Use with other queries for efficient lookups
     * const authorBookIds = store.query.ids('book', () => ({ authorId: { eq: 'author:leguin' } }))
     * ```
     *
     * @public
     */
    ids<TypeName extends R['typeName']>(typeName: TypeName, queryCreator?: () => QueryExpression<Extract<R, {
        typeName: TypeName;
    }>>, name?: string): Computed<Set<IdOf<Extract<R, {
        typeName: TypeName;
    }>>>, CollectionDiff<IdOf<Extract<R, {
        typeName: TypeName;
    }>>>>;
    /**
     * Executes a one-time query against the current store state and returns matching records.
     * This is a non-reactive query that returns results immediately without creating a computed value.
     * Use this when you need a snapshot of data at a specific point in time.
     *
     * @param typeName - The type name of records to query
     * @param query - The query expression object to match against
     * @returns An array of records that match the query at the current moment
     *
     * @example
     * ```ts
     * // Get current in-stock books (non-reactive)
     * const currentInStockBooks = store.query.exec('book', { inStock: { eq: true } })
     * console.log(currentInStockBooks) // Book[]
     *
     * // Unlike records(), this won't update when the data changes
     * const staticBookList = store.query.exec('book', { authorId: { eq: 'author:leguin' } })
     * ```
     *
     * @public
     */
    exec<TypeName extends R['typeName']>(typeName: TypeName, query: QueryExpression<Extract<R, {
        typeName: TypeName;
    }>>): Array<Extract<R, {
        typeName: TypeName;
    }>>;
}

/* Excluded from this release type: StoreRecord */

/**
 * Manages the schema definition, validation, and migration system for a Store.
 *
 * StoreSchema coordinates record types, handles data migrations between schema
 * versions, validates records, and provides the foundational structure for
 * reactive stores. It acts as the central authority for data consistency
 * and evolution within the store system.
 *
 * @example
 * ```ts
 * // Define record types
 * const Book = createRecordType<Book>('book', { scope: 'document' })
 * const Author = createRecordType<Author>('author', { scope: 'document' })
 *
 * // Create schema with migrations
 * const schema = StoreSchema.create(
 *   { book: Book, author: Author },
 *   {
 *     migrations: [bookMigrations, authorMigrations],
 *     onValidationFailure: (failure) => {
 *       console.warn('Validation failed, using default:', failure.error)
 *       return failure.record // or return a corrected version
 *     }
 *   }
 * )
 *
 * // Use with store
 * const store = new Store({ schema })
 * ```
 *
 * @public
 */
export declare class StoreSchema<R extends UnknownRecord, P = unknown> {
    readonly types: {
        [Record in R as Record['typeName']]: RecordType<R, any>;
    };
    private readonly options;
    /**
     * Creates a new StoreSchema with the given record types and options.
     *
     * This static factory method is the recommended way to create a StoreSchema.
     * It ensures type safety while providing a clean API for schema definition.
     *
     * @param types - Object mapping type names to their RecordType definitions
     * @param options - Optional configuration for migrations, validation, and integrity checking
     * @returns A new StoreSchema instance
     *
     * @example
     * ```ts
     * const Book = createRecordType<Book>('book', { scope: 'document' })
     * const Author = createRecordType<Author>('author', { scope: 'document' })
     *
     * const schema = StoreSchema.create(
     *   {
     *     book: Book,
     *     author: Author
     *   },
     *   {
     *     migrations: [bookMigrations],
     *     onValidationFailure: (failure) => failure.record
     *   }
     * )
     * ```
     *
     * @public
     */
    static create<R extends UnknownRecord, P = unknown>(types: {
        [TypeName in R['typeName']]: {
            createId: any;
        };
    }, options?: StoreSchemaOptions<R, P>): StoreSchema<R, P>;
    readonly migrations: Record<string, MigrationSequence>;
    readonly sortedMigrations: readonly Migration[];
    private readonly migrationCache;
    private constructor();
    /**
     * Validates a record using its corresponding RecordType validator.
     *
     * This method ensures that records conform to their type definitions before
     * being stored. If validation fails and an onValidationFailure handler is
     * provided, it will be called to potentially recover from the error.
     *
     * @param store - The store instance where validation is occurring
     * @param record - The record to validate
     * @param phase - The lifecycle phase where validation is happening
     * @param recordBefore - The previous version of the record (for updates)
     * @returns The validated record, potentially modified by validation failure handler
     *
     * @example
     * ```ts
     * try {
     *   const validatedBook = schema.validateRecord(
     *     store,
     *     { id: 'book:1', typeName: 'book', title: '', author: 'Jane Doe' },
     *     'createRecord',
     *     null
     *   )
     * } catch (error) {
     *   console.error('Record validation failed:', error)
     * }
     * ```
     *
     * @public
     */
    validateRecord(store: Store<R>, record: R, phase: 'createRecord' | 'initialize' | 'tests' | 'updateRecord', recordBefore: null | R): R;
    /**
     * Gets all migrations that need to be applied to upgrade from a persisted schema
     * to the current schema version.
     *
     * This method compares the persisted schema with the current schema and determines
     * which migrations need to be applied to bring the data up to date. It handles
     * both regular migrations and retroactive migrations, and caches results for
     * performance.
     *
     * @param persistedSchema - The schema version that was previously persisted
     * @returns A Result containing the list of migrations to apply, or an error message
     *
     * @example
     * ```ts
     * const persistedSchema = {
     *   schemaVersion: 2,
     *   sequences: { 'com.tldraw.book': 1, 'com.tldraw.author': 0 }
     * }
     *
     * const migrationsResult = schema.getMigrationsSince(persistedSchema)
     * if (migrationsResult.ok) {
     *   console.log('Migrations to apply:', migrationsResult.value.length)
     *   // Apply each migration to bring data up to date
     * }
     * ```
     *
     * @public
     */
    getMigrationsSince(persistedSchema: SerializedSchema): Result<Migration[], string>;
    /**
     * Migrates a single persisted record to match the current schema version.
     *
     * This method applies the necessary migrations to transform a record from an
     * older (or newer) schema version to the current version. It supports both
     * forward ('up') and backward ('down') migrations.
     *
     * @param record - The record to migrate
     * @param persistedSchema - The schema version the record was persisted with
     * @param direction - Direction to migrate ('up' for newer, 'down' for older)
     * @returns A MigrationResult containing the migrated record or an error
     *
     * @example
     * ```ts
     * const oldRecord = { id: 'book:1', typeName: 'book', title: 'Old Title', publishDate: '2020-01-01' }
     * const oldSchema = { schemaVersion: 2, sequences: { 'com.tldraw.book': 1 } }
     *
     * const result = schema.migratePersistedRecord(oldRecord, oldSchema, 'up')
     * if (result.type === 'success') {
     *   console.log('Migrated record:', result.value)
     *   // Record now has publishedYear instead of publishDate
     * } else {
     *   console.error('Migration failed:', result.reason)
     * }
     * ```
     *
     * @public
     */
    migratePersistedRecord(record: R, persistedSchema: SerializedSchema, direction?: 'down' | 'up'): MigrationResult<R>;
    migrateStorage(storage: SynchronousStorage<R>): void;
    /**
     * Migrates an entire store snapshot to match the current schema version.
     *
     * This method applies all necessary migrations to bring a persisted store
     * snapshot up to the current schema version. It handles both record-level
     * and store-level migrations, and can optionally mutate the input store
     * for performance.
     *
     * @param snapshot - The store snapshot containing data and schema information
     * @param opts - Options controlling migration behavior
     *   - mutateInputStore - Whether to modify the input store directly (default: false)
     * @returns A MigrationResult containing the migrated store or an error
     *
     * @example
     * ```ts
     * const snapshot = {
     *   schema: { schemaVersion: 2, sequences: { 'com.tldraw.book': 1 } },
     *   store: {
     *     'book:1': { id: 'book:1', typeName: 'book', title: 'Old Book', publishDate: '2020-01-01' }
     *   }
     * }
     *
     * const result = schema.migrateStoreSnapshot(snapshot)
     * if (result.type === 'success') {
     *   console.log('Migrated store:', result.value)
     *   // All records are now at current schema version
     * }
     * ```
     *
     * @public
     */
    migrateStoreSnapshot(snapshot: StoreSnapshot<R>, opts?: {
        mutateInputStore?: boolean;
    }): MigrationResult<SerializedStore<R>>;
    /* Excluded from this release type: createIntegrityChecker */
    /**
     * Serializes the current schema to a SerializedSchemaV2 format.
     *
     * This method creates a serialized representation of the current schema,
     * capturing the latest version number for each migration sequence.
     * The result can be persisted and later used to determine what migrations
     * need to be applied when loading data.
     *
     * @returns A SerializedSchemaV2 object representing the current schema state
     *
     * @example
     * ```ts
     * const serialized = schema.serialize()
     * console.log(serialized)
     * // {
     * //   schemaVersion: 2,
     * //   sequences: {
     * //     'com.tldraw.book': 3,
     * //     'com.tldraw.author': 2
     * //   }
     * // }
     *
     * // Store this with your data for future migrations
     * localStorage.setItem('schema', JSON.stringify(serialized))
     * ```
     *
     * @public
     */
    serialize(): SerializedSchemaV2;
    /* Excluded from this release type: serializeEarliestVersion */
    /* Excluded from this release type: getType */
}

/**
 * Configuration options for creating a StoreSchema.
 *
 * These options control migration behavior, validation error handling,
 * and integrity checking for the store schema.
 *
 * @example
 * ```ts
 * const options: StoreSchemaOptions<MyRecord, MyProps> = {
 *   migrations: [bookMigrations, authorMigrations],
 *   onValidationFailure: (failure) => {
 *     // Log the error and return a corrected record
 *     console.error('Validation failed:', failure.error)
 *     return sanitizeRecord(failure.record)
 *   },
 *   createIntegrityChecker: (store) => {
 *     // Set up integrity checking logic
 *     return setupIntegrityChecks(store)
 *   }
 * }
 * ```
 *
 * @public
 */
export declare interface StoreSchemaOptions<R extends UnknownRecord, P> {
    migrations?: MigrationSequence[];
    /** @public */
    onValidationFailure?(data: StoreValidationFailure<R>): R;
    /* Excluded from this release type: createIntegrityChecker */
}

/**
 * The side effect manager (aka a "correct state enforcer") is responsible
 * for making sure that the store's state is always correct and consistent. This includes
 * things like: deleting a shape if its parent is deleted; unbinding
 * arrows when their binding target is deleted; maintaining referential integrity; etc.
 *
 * Side effects are organized into lifecycle hooks that run before and after
 * record operations (create, change, delete), allowing you to validate data,
 * transform records, and maintain business rules.
 *
 * @example
 * ```ts
 * const sideEffects = new StoreSideEffects(store)
 *
 * // Ensure arrows are deleted when their target shape is deleted
 * sideEffects.registerAfterDeleteHandler('shape', (shape) => {
 *   const arrows = store.query.records('arrow', () => ({
 *     toId: { eq: shape.id }
 *   })).get()
 *   store.remove(arrows.map(arrow => arrow.id))
 * })
 * ```
 *
 * @public
 */
export declare class StoreSideEffects<R extends UnknownRecord> {
    private readonly store;
    /**
     * Creates a new side effects manager for the given store.
     *
     * store - The store instance to manage side effects for
     */
    constructor(store: Store<R>);
    private _beforeCreateHandlers;
    private _afterCreateHandlers;
    private _beforeChangeHandlers;
    private _afterChangeHandlers;
    private _beforeDeleteHandlers;
    private _afterDeleteHandlers;
    private _operationCompleteHandlers;
    private _isEnabled;
    /* Excluded from this release type: isEnabled */
    /* Excluded from this release type: setIsEnabled */
    /* Excluded from this release type: handleBeforeCreate */
    /* Excluded from this release type: handleAfterCreate */
    /* Excluded from this release type: handleBeforeChange */
    /* Excluded from this release type: handleAfterChange */
    /* Excluded from this release type: handleBeforeDelete */
    /* Excluded from this release type: handleAfterDelete */
    /* Excluded from this release type: handleOperationComplete */
    /* Excluded from this release type: register */
    /**
     * Register a handler to be called before a record of a certain type is created. Return a
     * modified record from the handler to change the record that will be created.
     *
     * Use this handle only to modify the creation of the record itself. If you want to trigger a
     * side-effect on a different record (for example, moving one shape when another is created),
     * use {@link StoreSideEffects.registerAfterCreateHandler} instead.
     *
     * @example
     * ```ts
     * editor.sideEffects.registerBeforeCreateHandler('shape', (shape, source) => {
     *     // only modify shapes created by the user
     *     if (source !== 'user') return shape
     *
     *     //by default, arrow shapes have no label. Let's make sure they always have a label.
     *     if (shape.type === 'arrow') {
     *         return {...shape, props: {...shape.props, text: 'an arrow'}}
     *     }
     *
     *     // other shapes get returned unmodified
     *     return shape
     * })
     * ```
     *
     * @param typeName - The type of record to listen for
     * @param handler - The handler to call
     *
     * @returns A callback that removes the handler.
     */
    registerBeforeCreateHandler<T extends R['typeName']>(typeName: T, handler: StoreBeforeCreateHandler<R & {
        typeName: T;
    }>): () => void;
    /**
     * Register a handler to be called after a record is created. This is useful for side-effects
     * that would update _other_ records. If you want to modify the record being created use
     * {@link StoreSideEffects.registerBeforeCreateHandler} instead.
     *
     * @example
     * ```ts
     * editor.sideEffects.registerAfterCreateHandler('page', (page, source) => {
     *     // Automatically create a shape when a page is created
     *     editor.createShape({
     *         id: createShapeId(),
     *         type: 'text',
     *         props: { richText: toRichText(page.name) },
     *     })
     * })
     * ```
     *
     * @param typeName - The type of record to listen for
     * @param handler - The handler to call
     *
     * @returns A callback that removes the handler.
     */
    registerAfterCreateHandler<T extends R['typeName']>(typeName: T, handler: StoreAfterCreateHandler<R & {
        typeName: T;
    }>): () => void;
    /**
     * Register a handler to be called before a record is changed. The handler is given the old and
     * new record - you can return a modified record to apply a different update, or the old record
     * to block the update entirely.
     *
     * Use this handler only for intercepting updates to the record itself. If you want to update
     * other records in response to a change, use
     * {@link StoreSideEffects.registerAfterChangeHandler} instead.
     *
     * @example
     * ```ts
     * editor.sideEffects.registerBeforeChangeHandler('shape', (prev, next, source) => {
     *     if (next.isLocked && !prev.isLocked) {
     *         // prevent shapes from ever being locked:
     *         return prev
     *     }
     *     // other types of change are allowed
     *     return next
     * })
     * ```
     *
     * @param typeName - The type of record to listen for
     * @param handler - The handler to call
     *
     * @returns A callback that removes the handler.
     */
    registerBeforeChangeHandler<T extends R['typeName']>(typeName: T, handler: StoreBeforeChangeHandler<R & {
        typeName: T;
    }>): () => void;
    /**
     * Register a handler to be called after a record is changed. This is useful for side-effects
     * that would update _other_ records - if you want to modify the record being changed, use
     * {@link StoreSideEffects.registerBeforeChangeHandler} instead.
     *
     * @example
     * ```ts
     * editor.sideEffects.registerAfterChangeHandler('shape', (prev, next, source) => {
     *     if (next.props.color === 'red') {
     *         // there can only be one red shape at a time:
     *         const otherRedShapes = editor.getCurrentPageShapes().filter(s => s.props.color === 'red' && s.id !== next.id)
     *         editor.updateShapes(otherRedShapes.map(s => ({...s, props: {...s.props, color: 'blue'}})))
     *     }
     * })
     * ```
     *
     * @param typeName - The type of record to listen for
     * @param handler - The handler to call
     *
     * @returns A callback that removes the handler.
     */
    registerAfterChangeHandler<T extends R['typeName']>(typeName: T, handler: StoreAfterChangeHandler<R & {
        typeName: T;
    }>): () => void;
    /**
     * Register a handler to be called before a record is deleted. The handler can return `false` to
     * prevent the deletion.
     *
     * Use this handler only for intercepting deletions of the record itself. If you want to do
     * something to other records in response to a deletion, use
     * {@link StoreSideEffects.registerAfterDeleteHandler} instead.
     *
     * @example
     * ```ts
     * editor.sideEffects.registerBeforeDeleteHandler('shape', (shape, source) => {
     *     if (shape.props.color === 'red') {
     *         // prevent red shapes from being deleted
     * 	       return false
     *     }
     * })
     * ```
     *
     * @param typeName - The type of record to listen for
     * @param handler - The handler to call
     *
     * @returns A callback that removes the handler.
     */
    registerBeforeDeleteHandler<T extends R['typeName']>(typeName: T, handler: StoreBeforeDeleteHandler<R & {
        typeName: T;
    }>): () => void;
    /**
     * Register a handler to be called after a record is deleted. This is useful for side-effects
     * that would update _other_ records - if you want to block the deletion of the record itself,
     * use {@link StoreSideEffects.registerBeforeDeleteHandler} instead.
     *
     * @example
     * ```ts
     * editor.sideEffects.registerAfterDeleteHandler('shape', (shape, source) => {
     *     // if the last shape in a frame is deleted, delete the frame too:
     *     const parentFrame = editor.getShape(shape.parentId)
     *     if (!parentFrame || parentFrame.type !== 'frame') return
     *
     *     const siblings = editor.getSortedChildIdsForParent(parentFrame)
     *     if (siblings.length === 0) {
     *         editor.deleteShape(parentFrame.id)
     *     }
     * })
     * ```
     *
     * @param typeName - The type of record to listen for
     * @param handler - The handler to call
     *
     * @returns A callback that removes the handler.
     */
    registerAfterDeleteHandler<T extends R['typeName']>(typeName: T, handler: StoreAfterDeleteHandler<R & {
        typeName: T;
    }>): () => void;
    /**
     * Register a handler to be called when a store completes an atomic operation.
     *
     * @example
     * ```ts
     * let count = 0
     *
     * editor.sideEffects.registerOperationCompleteHandler(() => count++)
     *
     * editor.selectAll()
     * expect(count).toBe(1)
     *
     * editor.store.atomic(() => {
     *	editor.selectNone()
     * 	editor.selectAll()
     * })
     *
     * expect(count).toBe(2)
     * ```
     *
     * @param handler - The handler to call
     *
     * @returns A callback that removes the handler.
     *
     * @public
     */
    registerOperationCompleteHandler(handler: StoreOperationCompleteHandler): () => void;
}

/**
 * A snapshot of the store including both data and schema information.
 * This enables proper migration when loading data from different schema versions.
 *
 * @example
 * ```ts
 * const snapshot = store.getStoreSnapshot()
 * // Later...
 * store.loadStoreSnapshot(snapshot)
 * ```
 *
 * @public
 */
export declare interface StoreSnapshot<R extends UnknownRecord> {
    /** The serialized store data */
    store: SerializedStore<R>;
    /** The serialized schema information */
    schema: SerializedSchema;
}

/**
 * Information about a record validation failure that occurred in the store.
 *
 * This interface provides context about validation errors, including the failed
 * record, the store state, and the operation phase where the failure occurred.
 * It's used by validation failure handlers to implement recovery strategies.
 *
 * @example
 * ```ts
 * const schema = StoreSchema.create(
 *   { book: Book },
 *   {
 *     onValidationFailure: (failure: StoreValidationFailure<Book>) => {
 *       console.error(`Validation failed during ${failure.phase}:`, failure.error)
 *       console.log('Failed record:', failure.record)
 *       console.log('Previous record:', failure.recordBefore)
 *
 *       // Return a corrected version of the record
 *       return { ...failure.record, title: failure.record.title || 'Untitled' }
 *     }
 *   }
 * )
 * ```
 *
 * @public
 */
export declare interface StoreValidationFailure<R extends UnknownRecord> {
    error: unknown;
    store: Store<R>;
    record: R;
    phase: 'createRecord' | 'initialize' | 'tests' | 'updateRecord';
    recordBefore: null | R;
}

/**
 * A validator for store records that ensures data integrity.
 * Validators are called when records are created or updated.
 *
 * @example
 * ```ts
 * const bookValidator: StoreValidator<Book> = {
 *   validate(record: unknown): Book {
 *     // Validate and return the record
 *     if (typeof record !== 'object' || !record.title) {
 *       throw new Error('Invalid book')
 *     }
 *     return record as Book
 *   }
 * }
 * ```
 *
 * @public
 */
export declare interface StoreValidator<R extends UnknownRecord> {
    /**
     * Validate a record.
     *
     * @param record - The record to validate
     * @returns The validated record
     * @throws When validation fails
     */
    validate(record: unknown): R;
    /**
     * Validate a record using a known good version for reference.
     *
     * @param knownGoodVersion - A known valid version of the record
     * @param record - The record to validate
     * @returns The validated record
     */
    validateUsingKnownGoodVersion?(knownGoodVersion: R, record: unknown): R;
}

/**
 * A map of validators for each record type in the store.
 *
 * @example
 * ```ts
 * const validators: StoreValidators<Book | Author> = {
 *   book: bookValidator,
 *   author: authorValidator
 * }
 * ```
 *
 * @public
 */
export declare type StoreValidators<R extends UnknownRecord> = {
    [K in R['typeName']]: StoreValidator<Extract<R, {
        typeName: K;
    }>>;
};

/**
 * Abstraction over the store that can be used to perform migrations.
 * @public
 */
export declare interface SynchronousRecordStorage<R extends UnknownRecord> {
    get(id: string): R | undefined;
    set(id: string, record: R): void;
    delete(id: string): void;
    keys(): Iterable<string>;
    values(): Iterable<R>;
    entries(): Iterable<[string, R]>;
}

/**
 * Abstraction over the storage that can be used to perform migrations.
 * @public
 */
export declare interface SynchronousStorage<R extends UnknownRecord> extends SynchronousRecordStorage<R> {
    getSchema(): SerializedSchema;
    setSchema(schema: SerializedSchema): void;
}

/**
 * A generic type representing any record that extends BaseRecord.
 * This is useful for type constraints when you need to work with records of unknown types,
 * but still want to ensure they follow the BaseRecord structure.
 *
 * @example
 * ```ts
 * // Function that works with any type of record
 * function logRecord(record: UnknownRecord): void {
 *   console.log(`Record ${record.id} of type ${record.typeName}`)
 * }
 *
 * // Can be used with any record type
 * const book: Book = { id: 'book:123' as RecordId<Book>, typeName: 'book', title: '1984' }
 * const author: Author = { id: 'author:456' as RecordId<Author>, typeName: 'author', name: 'Orwell' }
 *
 * logRecord(book)   // "Record book:123 of type book"
 * logRecord(author) // "Record author:456 of type author"
 * ```
 *
 * @public
 */
export declare type UnknownRecord = BaseRecord<string, RecordId<UnknownRecord>>;

export { }
