From 698f0b91fc4a494e2c2ca63a35e0c59eb5788394 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Mon, 15 Dec 2025 19:29:33 +0000 Subject: [PATCH] feat(transformers): Add onSkip flow control * Don't use newtransform when nothing has been assigned * Remove nanoid accidental commit --- src/backend/common/AbstractComponent.ts | 26 ++++++++++++++----- src/backend/common/errors/MSErrors.ts | 16 +++++++++++- .../common/infrastructure/Transform.ts | 1 + .../common/transforms/AbstractTransformer.ts | 5 ++-- .../transforms/MusicbrainzTransformer.ts | 4 +-- .../common/transforms/TransformerManager.ts | 5 ++-- src/backend/utils/ErrorUtils.ts | 4 +-- 7 files changed, 44 insertions(+), 17 deletions(-) diff --git a/src/backend/common/AbstractComponent.ts b/src/backend/common/AbstractComponent.ts index 11e539cf..4add7b3a 100644 --- a/src/backend/common/AbstractComponent.ts +++ b/src/backend/common/AbstractComponent.ts @@ -8,7 +8,7 @@ import { PlayData, PlayObject, TransformResult } from "../../core/Atomic.js"; import { buildPlayHumanDiffable, buildTrackString } from "../../core/StringUtils.js"; import { CommonClientConfig } from "./infrastructure/config/client/index.js"; import { CommonSourceConfig } from "./infrastructure/config/source/index.js"; -import { mergeSimpleError, TransformRulesError } from "./errors/MSErrors.js"; +import { mergeSimpleError, SkipTransformStageError, StageTransformError, TransformRulesError } from "./errors/MSErrors.js"; import { PlayTransformRules, StageConfig, @@ -23,6 +23,7 @@ import { nanoid } from "nanoid"; import {diffStringsUnified, DiffOptionsColor} from 'jest-diff'; import chalk from 'chalk'; import { isDebugMode } from "../utils.js"; +import { findCauseByReference } from "../utils/ErrorUtils.js"; export default abstract class AbstractComponent extends AbstractInitializable { @@ -192,20 +193,34 @@ export default abstract class AbstractComponent extends AbstractInitializable { const { onSuccess = 'continue', onFailure = 'stop', + onSkip = 'continue', failureReturnPartial = false } = hookItem; let newTransformedPlay: PlayObject, - stageName: string, + stageName: string = 'Unnamed', err: Error; try { [newTransformedPlay, stageName] = await this.transformManager.handleStage(hookItem, transformedPlay, asyncId); } catch (e) { err = e; + if(e instanceof StageTransformError) { + stageName = e.stageName; + } } if(err !== undefined) { - if(onFailure === 'continue') { + const skipError = findCauseByReference(err, SkipTransformStageError); + if(skipError !== undefined) { + let skipMsg = `Stage '${stageName}' was skipped`; + if(onSkip === 'stop') { + skipMsg += ' and will stop transform due to onSkip: stop'; + } + logger.debug(mergeSimpleError(err), skipMsg); + if(onSkip === 'stop') { + break; + } + } else if(onFailure === 'continue') { logger.warn(mergeSimpleError(err), 'A transform encountered an error but continuing due to onFailure: continue'); } else { logger.error(mergeSimpleError(err), 'Transform encountered an error'); @@ -222,12 +237,11 @@ export default abstract class AbstractComponent extends AbstractInitializable { name: stageName, play: newTransformedPlay.data }); + transformedPlay = newTransformedPlay; } - transformedPlay = newTransformedPlay; - if(err === undefined && onSuccess === 'stop') { - logger.debug(`${nanoid} Stopping transform due to onSuccess: stop`); + logger.debug(`Stopping transform due to onSuccess: stop`); break; } } diff --git a/src/backend/common/errors/MSErrors.ts b/src/backend/common/errors/MSErrors.ts index efbcc00b..3ebdab3b 100644 --- a/src/backend/common/errors/MSErrors.ts +++ b/src/backend/common/errors/MSErrors.ts @@ -36,6 +36,7 @@ const STACK_AT_REGEX = new RegExp(/[\n\r]\s*at/); export class SimpleError extends Error implements HasSimpleError { simple: boolean; + name = 'Error'; public constructor(msg: string, options?: ErrorOptions & { simple?: boolean, shortStack?: boolean }) { super(msg, options); @@ -56,6 +57,19 @@ export class SimpleError extends Error implements HasSimpleError { } } +export class StageTransformError extends NamedError { + name = 'Stage Transform'; + stageName: string; + constructor(name: string, message: string, options?: ErrorOptions) { + super(message, options); + this.stageName = name; + } +} + +export class SkipTransformStageError extends SimpleError { + name = 'Skip Transform Stage'; +} + export interface HasSimpleError extends Error { simple: boolean } @@ -68,7 +82,7 @@ export const isSimpleError = (e: unknown): e is HasSimpleError => { } export const mergeSimpleError = (err: Error): Error => { - const anySimple = findCauseByReference(err, SimpleError); + const anySimple = findCauseByFunc(err, (e) => isSimpleError(e)); if(anySimple && anySimple.simple) { return mergeErrorCause(err); } diff --git a/src/backend/common/infrastructure/Transform.ts b/src/backend/common/infrastructure/Transform.ts index 3971785b..cdba31e9 100644 --- a/src/backend/common/infrastructure/Transform.ts +++ b/src/backend/common/infrastructure/Transform.ts @@ -54,6 +54,7 @@ export type FlowControlTerm = 'continue' | 'stop' export interface FlowControl { onSuccess: FlowControlTerm onFailure: FlowControlTerm + onSkip: FlowControlTerm failureReturnPartial: boolean } diff --git a/src/backend/common/transforms/AbstractTransformer.ts b/src/backend/common/transforms/AbstractTransformer.ts index 95fd9e02..9fc81517 100644 --- a/src/backend/common/transforms/AbstractTransformer.ts +++ b/src/backend/common/transforms/AbstractTransformer.ts @@ -7,7 +7,7 @@ import { cacheFunctions, parseToRegexOrLiteralSearch, testMaybeRegex, searchAnd import { Cacheable } from "cacheable"; import { hashObject } from "../../utils/StringUtils.js"; import { playContentInvariantTransform } from "../../utils/PlayComparisonUtils.js"; -import { isSimpleError } from "../errors/MSErrors.js"; +import { isSimpleError, SkipTransformStageError } from "../errors/MSErrors.js"; import { capitalize } from "../../../core/StringUtils.js"; export interface TransformerOptions { @@ -71,9 +71,8 @@ export default abstract class AbstractTransformer boolean) => { +export const findCauseByFunc = (err: any, func: (e: Error) => boolean): T | undefined => { if (!err || !func) return; if (!(err instanceof Error)) return; if (typeof func !== 'function') { @@ -21,7 +21,7 @@ export const findCauseByFunc = (err: any, func: (e: Error) => boolean) => { seen.add(currentErr); if (func(currentErr)) { - return currentErr; + return currentErr as T; } currentErr = getErrorCause(currentErr); -- 2.51.2