96 lines
2.7 KiB
TypeScript
96 lines
2.7 KiB
TypeScript
import type {
|
|
IMigrationContext,
|
|
IMigrationStepDefinition,
|
|
} from './interfaces.js';
|
|
import type { SmartMigration } from './classes.smartmigration.js';
|
|
import { SmartMigrationError } from './classes.versionresolver.js';
|
|
|
|
/**
|
|
* Builder returned by `SmartMigration.step(id)`. The chain is:
|
|
* .from('1.0.0').to('1.1.0').description('...').resumable().up(async ctx => {...})
|
|
*
|
|
* `.up()` is the terminal call: it commits the step to its parent SmartMigration
|
|
* and returns that parent so further `.step(...)` calls can chain naturally.
|
|
*/
|
|
export class MigrationStepBuilder {
|
|
private parent: SmartMigration;
|
|
private id: string;
|
|
private _from: string | null = null;
|
|
private _to: string | null = null;
|
|
private _description: string | undefined;
|
|
private _resumable = false;
|
|
|
|
constructor(parent: SmartMigration, id: string) {
|
|
this.parent = parent;
|
|
this.id = id;
|
|
}
|
|
|
|
/** REQUIRED: the data version this step starts from. */
|
|
public from(version: string): this {
|
|
this._from = version;
|
|
return this;
|
|
}
|
|
|
|
/** REQUIRED: the data version this step ends at. */
|
|
public to(version: string): this {
|
|
this._to = version;
|
|
return this;
|
|
}
|
|
|
|
/** Optional human-readable description. */
|
|
public description(text: string): this {
|
|
this._description = text;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Mark the step as resumable. When set, the step's `up` handler receives a
|
|
* `ctx.checkpoint` object that persists arbitrary key/value state to the
|
|
* ledger between runs.
|
|
*/
|
|
public resumable(): this {
|
|
this._resumable = true;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Terminal: register the step on the parent SmartMigration and return the
|
|
* parent so that further steps can be chained.
|
|
*/
|
|
public up(handler: (ctx: IMigrationContext) => Promise<void>): SmartMigration {
|
|
if (this._from === null) {
|
|
throw new SmartMigrationError(
|
|
'STEP_MISSING_FROM',
|
|
`Migration step "${this.id}" is missing .from(version) before .up().`,
|
|
{ stepId: this.id },
|
|
);
|
|
}
|
|
if (this._to === null) {
|
|
throw new SmartMigrationError(
|
|
'STEP_MISSING_TO',
|
|
`Migration step "${this.id}" is missing .to(version) before .up().`,
|
|
{ stepId: this.id },
|
|
);
|
|
}
|
|
if (typeof handler !== 'function') {
|
|
throw new SmartMigrationError(
|
|
'STEP_HANDLER_NOT_FUNCTION',
|
|
`Migration step "${this.id}" was passed a non-function to .up().`,
|
|
{ stepId: this.id },
|
|
);
|
|
}
|
|
|
|
const definition: IMigrationStepDefinition = {
|
|
id: this.id,
|
|
fromVersion: this._from,
|
|
toVersion: this._to,
|
|
description: this._description,
|
|
isResumable: this._resumable,
|
|
handler,
|
|
};
|
|
|
|
this.parent.registerStep(definition);
|
|
return this.parent;
|
|
}
|
|
}
|