* feat(types): move Emigrate types to separate package Also refactor the types to use discriminating unions for easier error handling and such. Errors passed to storage plugins should now be serialized and storage plugins are expected to return already serialized errors on failed history entries. * fix(mysql): handle the new type changes * fix(storage-fs): handle the new type changes * feat(cli): better error handling and types Adapt to the new types from the @emigrate/types package, like discriminating union types and serializing and deserializing errors
160 lines
5.3 KiB
TypeScript
160 lines
5.3 KiB
TypeScript
import {
|
|
type SerializedError,
|
|
type MigrationMetadata,
|
|
type FailedMigrationMetadata,
|
|
type FailedMigrationHistoryEntry,
|
|
} from '@emigrate/types';
|
|
import { serializeError, errorConstructors, deserializeError } from 'serialize-error';
|
|
|
|
const formatter = new Intl.ListFormat('en', { style: 'long', type: 'disjunction' });
|
|
|
|
export const toError = (error: unknown) => (error instanceof Error ? error : new Error(String(error)));
|
|
|
|
export const toSerializedError = (error: unknown) => {
|
|
const errorInstance = toError(error);
|
|
|
|
return serializeError(errorInstance) as unknown as SerializedError;
|
|
};
|
|
|
|
export class EmigrateError extends Error {
|
|
constructor(
|
|
message: string | undefined,
|
|
options?: ErrorOptions,
|
|
public code?: string,
|
|
) {
|
|
super(message, options);
|
|
}
|
|
}
|
|
|
|
export class ShowUsageError extends EmigrateError {}
|
|
|
|
export class MissingOptionError extends ShowUsageError {
|
|
static fromOption(option: string | string[]) {
|
|
return new MissingOptionError(
|
|
`Missing required option: ${Array.isArray(option) ? formatter.format(option) : option}`,
|
|
undefined,
|
|
option,
|
|
);
|
|
}
|
|
|
|
constructor(
|
|
message: string | undefined,
|
|
options?: ErrorOptions,
|
|
public option: string | string[] = '',
|
|
) {
|
|
super(message, options, 'ERR_MISSING_OPT');
|
|
}
|
|
}
|
|
|
|
export class MissingArgumentsError extends ShowUsageError {
|
|
static fromArgument(argument: string) {
|
|
return new MissingArgumentsError(`Missing required argument: ${argument}`, undefined, argument);
|
|
}
|
|
|
|
constructor(
|
|
message: string | undefined,
|
|
options?: ErrorOptions,
|
|
public argument = '',
|
|
) {
|
|
super(message, options, 'ERR_MISSING_ARGS');
|
|
}
|
|
}
|
|
|
|
export class OptionNeededError extends ShowUsageError {
|
|
static fromOption(option: string, message: string) {
|
|
return new OptionNeededError(message, undefined, option);
|
|
}
|
|
|
|
constructor(
|
|
message: string | undefined,
|
|
options?: ErrorOptions,
|
|
public option = '',
|
|
) {
|
|
super(message, options, 'ERR_OPT_NEEDED');
|
|
}
|
|
}
|
|
|
|
export class BadOptionError extends ShowUsageError {
|
|
static fromOption(option: string, message: string) {
|
|
return new BadOptionError(message, undefined, option);
|
|
}
|
|
|
|
constructor(
|
|
message: string | undefined,
|
|
options?: ErrorOptions,
|
|
public option = '',
|
|
) {
|
|
super(message, options, 'ERR_BAD_OPT');
|
|
}
|
|
}
|
|
|
|
export class UnexpectedError extends EmigrateError {
|
|
constructor(message: string | undefined, options?: ErrorOptions) {
|
|
super(message, options, 'ERR_UNEXPECTED');
|
|
}
|
|
}
|
|
|
|
export class MigrationHistoryError extends EmigrateError {
|
|
static fromHistoryEntry(entry: FailedMigrationHistoryEntry) {
|
|
return new MigrationHistoryError(`Migration ${entry.name} is in a failed state, it should be fixed and removed`, {
|
|
cause: deserializeError(entry.error),
|
|
});
|
|
}
|
|
|
|
constructor(message: string | undefined, options?: ErrorOptions) {
|
|
super(message, options, 'ERR_MIGRATION_HISTORY');
|
|
}
|
|
}
|
|
|
|
export class MigrationLoadError extends EmigrateError {
|
|
static fromMetadata(metadata: MigrationMetadata, cause?: Error) {
|
|
return new MigrationLoadError(`Failed to load migration file: ${metadata.relativeFilePath}`, { cause });
|
|
}
|
|
|
|
constructor(message: string | undefined, options?: ErrorOptions) {
|
|
super(message, options, 'ERR_MIGRATION_LOAD');
|
|
}
|
|
}
|
|
|
|
export class MigrationRunError extends EmigrateError {
|
|
static fromMetadata(metadata: FailedMigrationMetadata) {
|
|
return new MigrationRunError(`Failed to run migration: ${metadata.relativeFilePath}`, { cause: metadata.error });
|
|
}
|
|
|
|
constructor(message: string | undefined, options?: ErrorOptions) {
|
|
super(message, options, 'ERR_MIGRATION_RUN');
|
|
}
|
|
}
|
|
|
|
export class MigrationNotRunError extends EmigrateError {
|
|
static fromMetadata(metadata: MigrationMetadata, cause?: Error) {
|
|
return new MigrationNotRunError(`Migration "${metadata.name}" is not in the migration history`, { cause });
|
|
}
|
|
|
|
constructor(message: string | undefined, options?: ErrorOptions) {
|
|
super(message, options, 'ERR_MIGRATION_NOT_RUN');
|
|
}
|
|
}
|
|
|
|
export class StorageInitError extends EmigrateError {
|
|
static fromError(error: Error) {
|
|
return new StorageInitError('Could not initialize storage', { cause: error });
|
|
}
|
|
|
|
constructor(message: string | undefined, options?: ErrorOptions) {
|
|
super(message, options, 'ERR_STORAGE_INIT');
|
|
}
|
|
}
|
|
|
|
errorConstructors.set('EmigrateError', EmigrateError as ErrorConstructor);
|
|
errorConstructors.set('ShowUsageError', ShowUsageError as ErrorConstructor);
|
|
errorConstructors.set('MissingOptionError', MissingOptionError as unknown as ErrorConstructor);
|
|
errorConstructors.set('MissingArgumentsError', MissingArgumentsError as unknown as ErrorConstructor);
|
|
errorConstructors.set('OptionNeededError', OptionNeededError as unknown as ErrorConstructor);
|
|
errorConstructors.set('BadOptionError', BadOptionError as unknown as ErrorConstructor);
|
|
errorConstructors.set('UnexpectedError', UnexpectedError as ErrorConstructor);
|
|
errorConstructors.set('MigrationHistoryError', MigrationHistoryError as unknown as ErrorConstructor);
|
|
errorConstructors.set('MigrationLoadError', MigrationLoadError as unknown as ErrorConstructor);
|
|
errorConstructors.set('MigrationRunError', MigrationRunError as unknown as ErrorConstructor);
|
|
errorConstructors.set('MigrationNotRunError', MigrationNotRunError as unknown as ErrorConstructor);
|
|
errorConstructors.set('StorageInitError', StorageInitError as unknown as ErrorConstructor);
|