UNPKG

object-migrations

Version:

Linear, in-memory migrations for versioned objects

171 lines (170 loc) 6.98 kB
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>>; }