feat(up): serialize errors before passing them to storage plugins
This commit is contained in:
parent
3b2b21f729
commit
c1d55978d7
7 changed files with 43 additions and 26 deletions
5
.changeset/friendly-bears-trade.md
Normal file
5
.changeset/friendly-bears-trade.md
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'@emigrate/storage-fs': minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Handle the serialized errors coming from Emigrate, so no need to serialize errors ourselves
|
||||||
5
.changeset/gold-suits-judge.md
Normal file
5
.changeset/gold-suits-judge.md
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'@emigrate/plugin-tools': minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Add serializeError utility function for serializing Error instances
|
||||||
5
.changeset/sixty-horses-kiss.md
Normal file
5
.changeset/sixty-horses-kiss.md
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'@emigrate/cli': minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Serialize errors before passing them to the storage so that storage plugins doesn't have to care about serialization of errors
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import process from 'node:process';
|
import process from 'node:process';
|
||||||
import { getOrLoadPlugins, getOrLoadReporter, getOrLoadStorage } from '@emigrate/plugin-tools';
|
import { getOrLoadPlugins, getOrLoadReporter, getOrLoadStorage, serializeError } from '@emigrate/plugin-tools';
|
||||||
import {
|
import {
|
||||||
type LoaderPlugin,
|
type LoaderPlugin,
|
||||||
type MigrationFunction,
|
type MigrationFunction,
|
||||||
|
|
@ -219,15 +219,16 @@ export default async function upCommand({
|
||||||
finishedMigrations.push(finishedMigration);
|
finishedMigrations.push(finishedMigration);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorInstance = error instanceof Error ? error : new Error(String(error));
|
const errorInstance = error instanceof Error ? error : new Error(String(error));
|
||||||
|
const serializedError = serializeError(errorInstance);
|
||||||
const duration = getDuration(start);
|
const duration = getDuration(start);
|
||||||
const finishedMigration: MigrationMetadataFinished = {
|
const finishedMigration: MigrationMetadataFinished = {
|
||||||
...migration,
|
...migration,
|
||||||
status: 'failed',
|
status: 'failed',
|
||||||
duration,
|
duration,
|
||||||
error: errorInstance,
|
error: serializedError,
|
||||||
};
|
};
|
||||||
|
|
||||||
await storage.onError(finishedMigration, errorInstance);
|
await storage.onError(finishedMigration, serializedError);
|
||||||
await reporter.onMigrationError?.(finishedMigration, errorInstance);
|
await reporter.onMigrationError?.(finishedMigration, errorInstance);
|
||||||
|
|
||||||
finishedMigrations.push(finishedMigration);
|
finishedMigrations.push(finishedMigration);
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,18 @@ import {
|
||||||
type EmigrateStorage,
|
type EmigrateStorage,
|
||||||
type LoaderPlugin,
|
type LoaderPlugin,
|
||||||
type StringOrModule,
|
type StringOrModule,
|
||||||
|
type SerializedError,
|
||||||
} from './types.js';
|
} from './types.js';
|
||||||
|
|
||||||
|
export const serializeError = (error: Error): SerializedError => {
|
||||||
|
return {
|
||||||
|
name: error.name,
|
||||||
|
message: error.message,
|
||||||
|
stack: error.stack,
|
||||||
|
cause: error.cause instanceof Error ? serializeError(error.cause) : error.cause,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export const isGeneratorPlugin = (plugin: any): plugin is GeneratorPlugin => {
|
export const isGeneratorPlugin = (plugin: any): plugin is GeneratorPlugin => {
|
||||||
if (!plugin || typeof plugin !== 'object') {
|
if (!plugin || typeof plugin !== 'object') {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,18 @@ export type StringOrModule<T> = string | T | (() => Awaitable<T>) | (() => Await
|
||||||
|
|
||||||
export type MigrationStatus = 'failed' | 'done' | 'pending';
|
export type MigrationStatus = 'failed' | 'done' | 'pending';
|
||||||
|
|
||||||
|
export type SerializedError = {
|
||||||
|
name: string;
|
||||||
|
message: string;
|
||||||
|
stack?: string;
|
||||||
|
cause?: unknown;
|
||||||
|
};
|
||||||
|
|
||||||
export type MigrationHistoryEntry = {
|
export type MigrationHistoryEntry = {
|
||||||
name: string;
|
name: string;
|
||||||
status: MigrationStatus;
|
status: MigrationStatus;
|
||||||
date: Date;
|
date: Date;
|
||||||
error?: unknown;
|
error?: SerializedError;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Storage = {
|
export type Storage = {
|
||||||
|
|
@ -66,7 +73,7 @@ export type Storage = {
|
||||||
* @param migration The name of the migration that should be marked as failed.
|
* @param migration The name of the migration that should be marked as failed.
|
||||||
* @param error The error that caused the migration to fail.
|
* @param error The error that caused the migration to fail.
|
||||||
*/
|
*/
|
||||||
onError(migration: MigrationMetadataFinished, error: Error): Promise<void>;
|
onError(migration: MigrationMetadataFinished, error: SerializedError): Promise<void>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type EmigrateStorage = {
|
export type EmigrateStorage = {
|
||||||
|
|
@ -142,7 +149,7 @@ export type MigrationMetadata = {
|
||||||
export type MigrationMetadataFinished = MigrationMetadata & {
|
export type MigrationMetadataFinished = MigrationMetadata & {
|
||||||
status: MigrationStatus | 'skipped';
|
status: MigrationStatus | 'skipped';
|
||||||
duration: number;
|
duration: number;
|
||||||
error?: Error;
|
error?: SerializedError;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type LoaderPlugin = {
|
export type LoaderPlugin = {
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,12 @@
|
||||||
import fs from 'node:fs/promises';
|
import fs from 'node:fs/promises';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import process from 'node:process';
|
import process from 'node:process';
|
||||||
import { type EmigrateStorage, type MigrationStatus } from '@emigrate/plugin-tools/types';
|
import { type SerializedError, type EmigrateStorage, type MigrationStatus } from '@emigrate/plugin-tools/types';
|
||||||
|
|
||||||
export type StorageFsOptions = {
|
export type StorageFsOptions = {
|
||||||
filename: string;
|
filename: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type SerializedError = {
|
|
||||||
name: string;
|
|
||||||
message: string;
|
|
||||||
stack?: string;
|
|
||||||
cause?: unknown;
|
|
||||||
};
|
|
||||||
|
|
||||||
const serializeError = (error: Error): SerializedError => {
|
|
||||||
return {
|
|
||||||
name: error.name,
|
|
||||||
message: error.message,
|
|
||||||
stack: error.stack,
|
|
||||||
cause: error.cause instanceof Error ? serializeError(error.cause) : error.cause,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function storageFs({ filename }: StorageFsOptions): EmigrateStorage {
|
export default function storageFs({ filename }: StorageFsOptions): EmigrateStorage {
|
||||||
const filePath = path.resolve(process.cwd(), filename);
|
const filePath = path.resolve(process.cwd(), filename);
|
||||||
const lockFilePath = `${filePath}.lock`;
|
const lockFilePath = `${filePath}.lock`;
|
||||||
|
|
@ -42,7 +26,7 @@ export default function storageFs({ filename }: StorageFsOptions): EmigrateStora
|
||||||
|
|
||||||
let lastUpdate: Promise<void> = Promise.resolve();
|
let lastUpdate: Promise<void> = Promise.resolve();
|
||||||
|
|
||||||
const update = async (migration: string, status: MigrationStatus, error?: Error) => {
|
const update = async (migration: string, status: MigrationStatus, error?: SerializedError) => {
|
||||||
lastUpdate = lastUpdate.then(async () => {
|
lastUpdate = lastUpdate.then(async () => {
|
||||||
const history = await read();
|
const history = await read();
|
||||||
|
|
||||||
|
|
@ -51,7 +35,7 @@ export default function storageFs({ filename }: StorageFsOptions): EmigrateStora
|
||||||
[migration]: {
|
[migration]: {
|
||||||
status,
|
status,
|
||||||
date: new Date().toISOString(),
|
date: new Date().toISOString(),
|
||||||
error: error ? serializeError(error) : undefined,
|
error,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -118,7 +102,7 @@ export default function storageFs({ filename }: StorageFsOptions): EmigrateStora
|
||||||
name,
|
name,
|
||||||
status,
|
status,
|
||||||
date: new Date(date),
|
date: new Date(date),
|
||||||
error: error ? new Error(error.message) : undefined,
|
error,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue