import { type Class } from './util.js';
export type Version = string | number | symbol | Function;
export type SyncMigration<TFrom, TTo> = (fromObject: TFrom) => TTo;
export type AsyncMigration<TFrom, TTo> = (fromObject: TFrom) => Promise<TTo>;
export type Migration<TFrom, TTo> = SyncMigration<TFrom, TTo> | AsyncMigration<TFrom, TTo>;
/**
 * Result of a successful migration.
 */
export interface Migrated<TTo> {
    /**
     * The migrated object.
     */
    readonly value: TTo;
    /**
     * Whether the object changed during the migration. `false` if the object was already at the
     * version that it was attempted to be migrated to, in which case {@linkcode value} is the
     * initial (unchanged) object.
     */
    readonly changed: boolean;
}
/**
 * Allows registering migrations between object versions and then migrating objects forward and
 * backward accordingly.
 *
 * @remarks
 *
 * - First register migrations with {@linkcode register}. Then, migrate objects forward
 *   with {@linkcode forward}/{@linkcode forwardAsync} and backward with
 *   {@linkcode backward}/{@linkcode backwardAsync}.
 *
 *   ```typescript
 *   const m = new Migrator();
 *
 *   m.register(1, 2, ...);
 *   m.register(2, 3, ...);
 *
 *   const v2 = m.forward<V2>(v1Object, 1, 2);
 *   ```
 *
 * - A record of version-type mappings can be optionally specified, so that subsequent
 *   {@linkcode register}, {@linkcode forward}, {@linkcode forwardAsync}, {@linkcode backward}, and
 *   {@linkcode backwardAsync} calls are typed accordingly.
 *
 *   ```typescript
 *   const m = new Migrator<{
 *       1: V1,
 *       2: V2,
 *   }>();
 *
 *   m.register(1, 2, ...);
 *
 *   const v2: V2 = m.forward(v1Object, 1, 2);
 *   ```
 */
export declare class Migrator<TVersions = unknown> {
    /**
     * Registers the forward and optional backward migrations between two successive versions.
     *
     * @remarks
     *
     * - Versions are strings, numbers or symbols.
     *
     *   ```typescript
     *   register<V1, V2>(1, 2, (v1) => makeV2(), (v2) => makeV1());
     *   ```
     *
     * - If the objects are instances of classes, the class (type) itself can be used as the version,
     *   and the parameter & return types of the migrations will be inferred accordingly.
     *
     *   ```typescript
     *   register(V1, V2, (v1) => new V2(...), (v2) => new V1(...));
     *   ```
     *
     * - Migrations can be synchronous or asynchronous. {@linkcode forward} & {@linkcode backward}
     *   work only with synchronous migrations. {@linkcode forwardAsync} & {@linkcode backwardAsync}
     *   work with both.
     *
     *   ```typescript
     *   register(V1, V2, (v1) => getV2(), (v2) => getV1());
     *   register(V1, V2, async (v1) => await getV2Async(), async (v2) => await getV1Async());
     *   ```
     */
    register<TFrom, TTo>(fromClass: Class<TFrom>, toClass: Class<TTo>, forward: Migration<TFrom, TTo>, backward?: Migration<TTo, TFrom>): void;
    register<TFrom extends keyof TVersions, TTo extends keyof TVersions>(fromVersion: TFrom, toVersion: TTo, forward: Migration<TVersions[TFrom], TVersions[TTo]>, backward?: Migration<TVersions[TTo], TVersions[TFrom]>): void;
    register<TFrom, TTo>(fromVersion: Version, toVersion: Version, forward: Migration<TFrom, TTo>, backward?: Migration<TTo, TFrom>): void;
    /**
     * Migrates an object forward between two versions.
     *
     * @remarks
     *
     * - Use the 3-parameter overload to specify the object and the versions it is being migrated in
     *   between.
     *
     *   ```typescript
     *   const v2 = forward<V2>(v1Object, 1, 2);
     *   ```
     *
     * - If version-type mappings are specified, the return type will be inferred accordingly.
     *
     *   ```typescript
     *   const v2 = forward(v1Object, 1, 2);
     *   ```
     *
     * - If the objects are instances of classes and their classes were {@link register registered}
     *   as their versions, use the 2-parameter overload.
     *
     *   ```typescript
     *   const v2 = forward(v1Object, V2);
     *   ```
     *
     * - Immediately returns a result with the same object, if it is already at the version it is
     *   being migrated to.
     *
     * - To run asynchronously, use {@link forwardAsync} instead.
     *
     * - Throws {@linkcode NoMigrationStepsError}, {@linkcode MigrationError}.
     */
    forward<TTo>(obj: object, toClass: Class<TTo>): Migrated<TTo>;
    forward<TTo extends keyof TVersions>(obj: object, fromVersion: Version, toVersion: TTo): Migrated<TVersions[TTo]>;
    forward<TTo>(obj: object, fromVersion: Version, toVersion: Version): Migrated<TTo>;
    /**
     * Asynchronously migrates an object forward between two versions. Like {@linkcode forward}, but
     * supports both synchronous and asynchronous migrations.
     */
    forwardAsync<TTo>(obj: object, toClass: Class<TTo>): Promise<Migrated<TTo>>;
    forwardAsync<TTo extends keyof TVersions>(obj: object, fromVersion: Version, toVersion: TTo): Promise<Migrated<TVersions[TTo]>>;
    forwardAsync<TTo>(obj: object, fromVersion: Version, toVersion: Version): Promise<Migrated<TTo>>;
    /**
     * Migrates an object backward between two versions.
     *
     * @remarks
     *
     * - Use the 3-parameter overload to specify the object and the versions it is being migrated in
     *   between.
     *
     *   ```typescript
     *   const v2 = backward<V2>(v1Object, 1, 2);
     *   ```
     *
     * - If version-type mappings are specified, the return type will be inferred accordingly.
     *
     *   ```typescript
     *   const v2 = backward(v1Object, 1, 2);
     *   ```
     *
     * - If the objects are instances of classes and their classes were {@link register registered}
     *   as their versions, use the 2-parameter overload.
     *
     *   ```typescript
     *   const v2 = backward(v1Object, V2);
     *   ```
     *
     * - Immediately returns a result with the same object, if it is already at the version it is
     *   being migrated to.
     *
     * - To run asynchronously, use {@link backwardAsync} instead.
     *
     * - Throws {@linkcode NoMigrationStepsError}, {@linkcode MigrationError}.
     */
    backward<TTo>(obj: object, toClass: Class<TTo>): Migrated<TTo>;
    backward<TTo extends keyof TVersions>(obj: object, fromVersion: Version, toVersion: TTo): Migrated<TVersions[TTo]>;
    backward<TTo>(obj: object, fromVersion: Version, toVersion: Version): Migrated<TTo>;
    /**
     * Asynchronously migrates an object backward between two versions. Like {@linkcode backward},
     * but supports both synchronous and asynchronous migrations.
     */
    backwardAsync<TTo>(obj: object, toClass: Class<TTo>): Promise<Migrated<TTo>>;
    backwardAsync<TTo extends keyof TVersions>(obj: object, fromVersion: Version, toVersion: TTo): Promise<Migrated<TVersions[TTo]>>;
    backwardAsync<TTo>(obj: object, fromVersion: Version, toVersion: Version): Promise<Migrated<TTo>>;
}
