import { ValidateExVer } from '../../../base/lib/exver' import * as T from '../../../base/lib/types' /** * Sentinel value indicating that a migration in a given direction is not possible. * Use this for `migrations.up` or `migrations.down` to prevent migration. */ export const IMPOSSIBLE: unique symbol = Symbol('IMPOSSIBLE') /** * Configuration options for a single service version definition. * * @typeParam Version - The string literal exver version number */ export type VersionOptions = { /** The exver-compliant version number */ version: Version & ValidateExVer /** The release notes for this version */ releaseNotes: T.LocaleString /** Data migrations for this version */ migrations: { /** * A migration from the previous version. Leave empty to indicate no migration is necessary. * Set to `IMPOSSIBLE` to indicate migrating from the previous version is not possible. */ up?: ((opts: { effects: T.Effects }) => Promise) | typeof IMPOSSIBLE /** * A migration to the previous version. Leave blank to indicate no migration is necessary. * Set to `IMPOSSIBLE` to indicate downgrades are prohibited */ down?: ((opts: { effects: T.Effects }) => Promise) | typeof IMPOSSIBLE /** * Additional migrations, such as fast-forward migrations, or migrations from other flavors. */ other?: Record< string, { up?: (opts: { effects: T.Effects }) => Promise down?: (opts: { effects: T.Effects }) => Promise } > } } /** * Represents a single version of the service, including its release notes, * migration scripts, and backwards-compatibility declarations. * * By convention, each version gets its own file (e.g. `versions/v1_0_0.ts`). * * @typeParam Version - The string literal exver version number */ export class VersionInfo { private _version: null | Version = null private constructor( readonly options: VersionOptions & { satisfies: string[] }, ) {} /** * @description Use this function to define a new version of the service. By convention, each version should receive its own file. * @property {string} version * @property {string} releaseNotes * @property {object} migrations * @returns A VersionInfo class instance that is exported, then imported into versions/index.ts. */ static of(options: VersionOptions) { return new VersionInfo({ ...options, satisfies: [] }) } /** Specify a version that this version is 100% backwards compatible to */ satisfies( version: V & ValidateExVer, ): VersionInfo { return new VersionInfo({ ...this.options, satisfies: [...this.options.satisfies, version], }) } } function __type_tests() { const version: VersionInfo<'1.0.0:0'> = VersionInfo.of({ version: '1.0.0:0', releaseNotes: '', migrations: {}, }) .satisfies('#other:1.0.0:0') .satisfies('#other:2.0.0:0') // @ts-expect-error .satisfies('#other:2.f.0:0') let a: VersionInfo<'1.0.0:0'> = version // @ts-expect-error let b: VersionInfo<'1.0.0:3'> = version VersionInfo.of({ // @ts-expect-error version: 'test', releaseNotes: '', migrations: {}, }) VersionInfo.of({ // @ts-expect-error version: 'test' as string, releaseNotes: '', migrations: {}, }) }