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): 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; } }