object-migrations
Version:
Linear, in-memory migrations for versioned objects
171 lines (170 loc) • 6.98 kB
TypeScript
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>>;
}