From 7e6eddce3e659c3b4d3cdce644cbec7f15492cc3 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Thu, 28 Mar 2024 09:04:40 -0400 Subject: [PATCH] refactor: Reduce usage of ErrorWithCause Error Cause (https://github.com/tc39/proposal-error-cause) was finalized and introduced in ES2022 so we don't need to ponyfill this anymore. Still using the helpful helper functions thought. --- src/backend/common/errors/UpstreamError.ts | 3 +- src/backend/common/vendor/JRiverApiClient.ts | 5 ++-- src/backend/common/vendor/KodiApiClient.ts | 3 +- src/backend/common/vendor/LastfmApiClient.ts | 3 +- .../chromecast/ChromecastClientUtils.ts | 7 ++--- src/backend/index.ts | 5 ++-- .../scrobblers/AbstractScrobbleClient.ts | 10 +++---- src/backend/scrobblers/LastfmScrobbler.ts | 3 +- .../scrobblers/ListenbrainzScrobbler.ts | 3 +- src/backend/scrobblers/MalojaScrobbler.ts | 12 ++++---- src/backend/server/index.ts | 7 ++--- src/backend/sources/AbstractSource.ts | 13 ++++---- src/backend/sources/ChromecastSource.ts | 30 +++++++++---------- src/backend/sources/DeezerSource.ts | 3 +- src/backend/sources/LastfmSource.ts | 7 ++--- src/backend/sources/ListenbrainzSource.ts | 7 ++--- src/backend/sources/MPRISSource.ts | 11 ++++--- src/backend/sources/MopidySource.ts | 3 +- src/backend/sources/SpotifySource.ts | 10 ++----- src/backend/sources/SubsonicSource.ts | 5 +--- src/backend/utils.ts | 14 ++++----- src/backend/utils/MDNSUtils.ts | 9 +++--- 22 files changed, 74 insertions(+), 99 deletions(-) diff --git a/src/backend/common/errors/UpstreamError.ts b/src/backend/common/errors/UpstreamError.ts index e2baae73..452385a3 100644 --- a/src/backend/common/errors/UpstreamError.ts +++ b/src/backend/common/errors/UpstreamError.ts @@ -1,8 +1,7 @@ -import {ErrorWithCause} from "pony-cause"; import { findCauseByFunc } from "../../utils.js"; import {Response} from 'superagent'; -export class UpstreamError extends ErrorWithCause { +export class UpstreamError extends Error { showStopper: boolean = false; response?: Response diff --git a/src/backend/common/vendor/JRiverApiClient.ts b/src/backend/common/vendor/JRiverApiClient.ts index 9868398d..a9d40f9a 100644 --- a/src/backend/common/vendor/JRiverApiClient.ts +++ b/src/backend/common/vendor/JRiverApiClient.ts @@ -2,7 +2,6 @@ import AbstractApiClient from "./AbstractApiClient.js"; import {JRiverData} from "../infrastructure/config/source/jriver.js"; import request, {Request, Response} from 'superagent'; import xml2js from 'xml2js'; -import {ErrorWithCause} from "pony-cause"; import {AbstractApiOptions, DEFAULT_RETRY_MULTIPLIER} from "../infrastructure/Atomic.js"; const parser = new xml2js.Parser({'async': true}); @@ -134,7 +133,7 @@ export class JRiverApiClient extends AbstractApiClient { this.logger.verbose(`Found ${data.ProgramName} ${data.ProgramVersion} (${data.FriendlyName})`); return true; } catch (e) { - throw new ErrorWithCause('Could not communicate with JRiver server. Verify your server URL is correct.', {cause: e}); + throw new Error('Could not communicate with JRiver server. Verify your server URL is correct.', {cause: e}); } } @@ -152,7 +151,7 @@ export class JRiverApiClient extends AbstractApiClient { if(this.config.username === undefined || this.config.password === undefined) { msg = 'Authentication failed. No username/password was provided in config! Did you mean to do this?'; } - this.logger.error(new ErrorWithCause(msg, {cause: e})); + this.logger.error(new Error(msg, {cause: e})); return false; } } diff --git a/src/backend/common/vendor/KodiApiClient.ts b/src/backend/common/vendor/KodiApiClient.ts index 4f61e6f6..61b63eb8 100644 --- a/src/backend/common/vendor/KodiApiClient.ts +++ b/src/backend/common/vendor/KodiApiClient.ts @@ -1,5 +1,4 @@ import AbstractApiClient from "./AbstractApiClient.js"; -import {ErrorWithCause} from "pony-cause"; import { KodiData } from "../infrastructure/config/source/kodi.js"; import { KodiClient } from 'kodi-api' import normalizeUrl from "normalize-url"; @@ -143,7 +142,7 @@ export class KodiApiClient extends AbstractApiClient { if(this.config.username === undefined || this.config.password === undefined) { msg = 'Authentication failed. No username/password was provided in config! Did you mean to do this?'; } - this.logger.error(new ErrorWithCause(msg, {cause: e})); + this.logger.error(new Error(msg, {cause: e})); return false; } } diff --git a/src/backend/common/vendor/LastfmApiClient.ts b/src/backend/common/vendor/LastfmApiClient.ts index d31d7c4c..4f0d6595 100644 --- a/src/backend/common/vendor/LastfmApiClient.ts +++ b/src/backend/common/vendor/LastfmApiClient.ts @@ -13,7 +13,6 @@ import { LastfmData } from "../infrastructure/config/client/lastfm.js"; import { PlayObject } from "../../../core/Atomic.js"; import {getNodeNetworkException, isNodeNetworkException} from "../errors/NodeErrors.js"; import {nonEmptyStringOrDefault, splitByFirstFound} from "../../../core/StringUtils.js"; -import {ErrorWithCause} from "pony-cause"; import {getScrobbleTsSOCDate} from "../../utils/TimeUtils.js"; import {UpstreamError} from "../errors/UpstreamError.js"; @@ -168,7 +167,7 @@ export default class LastfmApiClient extends AbstractApiClient { } return true; } catch (e) { - throw new ErrorWithCause('Current lastfm credentials file exists but could not be parsed', {cause: e}); + throw new Error('Current lastfm credentials file exists but could not be parsed', {cause: e}); } } diff --git a/src/backend/common/vendor/chromecast/ChromecastClientUtils.ts b/src/backend/common/vendor/chromecast/ChromecastClientUtils.ts index 227726e4..cfcd11a3 100644 --- a/src/backend/common/vendor/chromecast/ChromecastClientUtils.ts +++ b/src/backend/common/vendor/chromecast/ChromecastClientUtils.ts @@ -1,7 +1,6 @@ import { REPORTED_PLAYER_STATUSES, ReportedPlayerStatus } from "../../infrastructure/Atomic.js"; import { PlatformApplication, PlatformType } from "./interfaces.js"; import {Media, MediaController, Result} from "@foxxmd/chromecast-client"; -import {ErrorWithCause} from "pony-cause"; import objectHash from "object-hash"; import { PlayObject } from "../../../../core/Atomic.js"; @@ -27,7 +26,7 @@ export const getCurrentPlatformApplications = async (platform: PlatformType): Pr try { statusRes = await platform.getStatus() } catch (e) { - throw new ErrorWithCause('Unable to fetch platform statuses', {cause: e}); + throw new Error('Unable to fetch platform statuses', {cause: e}); } let status: {applications?: PlatformApplication[]}; @@ -39,7 +38,7 @@ export const getCurrentPlatformApplications = async (platform: PlatformType): Pr } return status.applications; } catch (e) { - throw new ErrorWithCause('Unable to fetch platform statuses', {cause: e}); + throw new Error('Unable to fetch platform statuses', {cause: e}); } } @@ -51,7 +50,7 @@ export const getMediaStatus = async (controller: MediaController.MediaController status = statusRes.unwrapAndThrow(); return status; } catch (e) { - throw new ErrorWithCause('Unable to fetch media status', {cause: e}); + throw new Error('Unable to fetch media status', {cause: e}); } } diff --git a/src/backend/index.ts b/src/backend/index.ts index 559687fd..340bc2e4 100644 --- a/src/backend/index.ts +++ b/src/backend/index.ts @@ -17,7 +17,6 @@ import { initServer } from "./server/index.js"; import {SimpleIntervalJob, ToadScheduler} from "toad-scheduler"; import { createHeartbeatSourcesTask } from "./tasks/heartbeatSources.js"; import { createHeartbeatClientsTask } from "./tasks/heartbeatClients.js"; -import {ErrorWithCause} from "pony-cause"; import {loggerDebug, childLogger, LogData, Logger as FoxLogger} from '@foxxmd/logging'; dayjs.extend(utc) @@ -42,7 +41,7 @@ output = output.slice(0, 301); let logger: FoxLogger; process.on('uncaughtExceptionMonitor', (err, origin) => { - const appError = new ErrorWithCause(`Uncaught exception is crashing the app! :( Type: ${origin}`, {cause: err}); + const appError = new Error(`Uncaught exception is crashing the app! :( Type: ${origin}`, {cause: err}); if(logger !== undefined) { logger.error(appError) } else { @@ -149,7 +148,7 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) logger.info('Scheduler started.'); } catch (e) { - const appError = new ErrorWithCause('Exited with uncaught error', {cause: e}); + const appError = new Error('Exited with uncaught error', {cause: e}); if(logger !== undefined) { logger.error(appError); } else { diff --git a/src/backend/scrobblers/AbstractScrobbleClient.ts b/src/backend/scrobblers/AbstractScrobbleClient.ts index c8e8d1ca..fc78a737 100644 --- a/src/backend/scrobblers/AbstractScrobbleClient.ts +++ b/src/backend/scrobblers/AbstractScrobbleClient.ts @@ -41,7 +41,7 @@ import EventEmitter from "events"; import { compareScrobbleArtists, compareScrobbleTracks, normalizeStr } from "../utils/StringUtils.js"; import { hasUpstreamError, UpstreamError } from "../common/errors/UpstreamError.js"; import {nanoid} from "nanoid"; -import {ErrorWithCause, messageWithCauses} from "pony-cause"; +import {messageWithCauses} from "pony-cause"; import { hasNodeNetworkException } from "../common/errors/NodeErrors.js"; import { comparePlayTemporally, @@ -187,7 +187,7 @@ export default abstract class AbstractScrobbleClient implements Authenticatable // only signal as auth failure if error was NOT either a node network error or a non-showstopping upstream error this.authFailure = !(hasNodeNetworkException(e) || hasUpstreamError(e, false)); this.authed = false; - this.logger.error(new ErrorWithCause(`Authentication test failed!${this.authFailure === false ? ' Due to a network issue. Will retry authentication on next heartbeat.' : ''}`, {cause: e})); + this.logger.error(new Error(`Authentication test failed!${this.authFailure === false ? ' Due to a network issue. Will retry authentication on next heartbeat.' : ''}`, {cause: e})); } } @@ -583,10 +583,10 @@ ${closestMatch.breakdowns.join('\n')}`, {leaf: ['Dupe Check']}); } catch (e) { if (e instanceof UpstreamError && e.showStopper === false) { this.addDeadLetterScrobble(currQueuedPlay, e); - this.logger.warn(new ErrorWithCause(`Could not scrobble ${buildTrackString(currQueuedPlay.play)} from Source '${currQueuedPlay.source}' but error was not show stopping. Adding scrobble to Dead Letter Queue and will retry on next heartbeat.`, {cause: e})); + this.logger.warn(new Error(`Could not scrobble ${buildTrackString(currQueuedPlay.play)} from Source '${currQueuedPlay.source}' but error was not show stopping. Adding scrobble to Dead Letter Queue and will retry on next heartbeat.`, {cause: e})); } else { this.queuedScrobbles.unshift(currQueuedPlay); - throw new ErrorWithCause('Error occurred while trying to scrobble', {cause: e}); + throw new Error('Error occurred while trying to scrobble', {cause: e}); } } } else if (!timeFrameValid) { @@ -666,7 +666,7 @@ ${closestMatch.breakdowns.join('\n')}`, {leaf: ['Dupe Check']}); deadScrobble.retries++; deadScrobble.error = messageWithCauses(e); deadScrobble.lastRetry = dayjs(); - this.logger.error(new ErrorWithCause(`Could not scrobble ${buildTrackString(deadScrobble.play)} from Source '${deadScrobble.source}' due to error`, {cause: e})); + this.logger.error(new Error(`Could not scrobble ${buildTrackString(deadScrobble.play)} from Source '${deadScrobble.source}' due to error`, {cause: e})); this.deadLetterScrobbles[deadScrobbleIndex] = deadScrobble; return [false, deadScrobble]; } finally { diff --git a/src/backend/scrobblers/LastfmScrobbler.ts b/src/backend/scrobblers/LastfmScrobbler.ts index 8ba0a867..7f776f0c 100644 --- a/src/backend/scrobblers/LastfmScrobbler.ts +++ b/src/backend/scrobblers/LastfmScrobbler.ts @@ -20,7 +20,6 @@ import EventEmitter from "events"; import { UpstreamError } from "../common/errors/UpstreamError.js"; import { isNodeNetworkException } from "../common/errors/NodeErrors.js"; import { getScrobbleTsSOCDate } from "../utils/TimeUtils.js"; -import {ErrorWithCause} from "pony-cause"; export default class LastfmScrobbler extends AbstractScrobbleClient { @@ -47,7 +46,7 @@ export default class LastfmScrobbler extends AbstractScrobbleClient { this.logger.info('Initialized'); } catch (e) { this.initialized = false; - this.logger.warn(new ErrorWithCause('Initialization failed', {cause: e})); + this.logger.warn(new Error('Initialization failed', {cause: e})); } return this.initialized; diff --git a/src/backend/scrobblers/ListenbrainzScrobbler.ts b/src/backend/scrobblers/ListenbrainzScrobbler.ts index c6355421..81c7c0a7 100644 --- a/src/backend/scrobblers/ListenbrainzScrobbler.ts +++ b/src/backend/scrobblers/ListenbrainzScrobbler.ts @@ -11,7 +11,6 @@ import { buildTrackString, capitalize } from "../../core/StringUtils.js"; import EventEmitter from "events"; import { UpstreamError } from "../common/errors/UpstreamError.js"; import { isNodeNetworkException } from "../common/errors/NodeErrors.js"; -import {ErrorWithCause} from "pony-cause"; export default class ListenbrainzScrobbler extends AbstractScrobbleClient { @@ -40,7 +39,7 @@ export default class ListenbrainzScrobbler extends AbstractScrobbleClient { this.initialized = true; this.logger.info('Initialized'); } catch (e) { - this.logger.warn(new ErrorWithCause('Could not initialize', {cause: e})); + this.logger.warn(new Error('Could not initialize', {cause: e})); this.initialized = false; } } diff --git a/src/backend/scrobblers/MalojaScrobbler.ts b/src/backend/scrobblers/MalojaScrobbler.ts index 9cb27e6e..9d498a96 100644 --- a/src/backend/scrobblers/MalojaScrobbler.ts +++ b/src/backend/scrobblers/MalojaScrobbler.ts @@ -24,9 +24,7 @@ import { buildTrackString, capitalize } from "../../core/StringUtils.js"; import EventEmitter from "events"; import normalizeUrl from "normalize-url"; import { UpstreamError } from "../common/errors/UpstreamError.js"; -import {ErrorWithCause} from "pony-cause"; import { getScrobbleTsSOCDate, getScrobbleTsSOCDateWithContext } from "../utils/TimeUtils.js"; -import e from "express"; import { isNodeNetworkException } from "../common/errors/NodeErrors.js"; import { isSuperAgentResponseError } from "../common/errors/ErrorUtils.js"; @@ -170,7 +168,7 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { throw new UpstreamError(`API Call failed (HTTP ${status}) => ${message}`, {cause: e}) } } else { - throw new ErrorWithCause('Unexpected error occurred during API call', {cause : e}); + throw new Error('Unexpected error occurred during API call', {cause : e}); } } } @@ -208,7 +206,7 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { } return true; } catch (e) { - this.logger.error(new ErrorWithCause('Communication test failed', {cause: e})); + this.logger.error(new Error('Communication test failed', {cause: e})); return false; } } @@ -244,7 +242,7 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { return [true]; } catch (e) { - this.logger.error(new ErrorWithCause('Unexpected error encountered while testing server health', {cause: e})); + this.logger.error(new Error('Unexpected error encountered while testing server health', {cause: e})); throw e; } } @@ -293,7 +291,7 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { } } catch (e) { if(e instanceof UpstreamError) { - if(e.cause.status === 403) { + if((e?.cause as any)?.status === 403) { // may be an older version that doesn't support auth readiness before db upgrade // and if it was before api was accessible during db build then test would fail during testConnection() if(compareVersions(this.serverVersion, '2.12.19') < 0) { @@ -322,7 +320,7 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { this.serverIsHealthy = true; } } catch (e) { - this.logger.error(new ErrorWithCause(`Testing server health failed due to an unexpected error`, {cause: e})); + this.logger.error(new Error(`Testing server health failed due to an unexpected error`, {cause: e})); this.serverIsHealthy = false; } return this.serverIsHealthy diff --git a/src/backend/server/index.ts b/src/backend/server/index.ts index eb6330a5..4c60232d 100644 --- a/src/backend/server/index.ts +++ b/src/backend/server/index.ts @@ -8,7 +8,6 @@ import { getRoot } from "../ioc.js"; import { setupApi } from "./api.js"; import { getAddress, mergeArr, parseBool } from "../utils.js"; import {stripIndents} from "common-tags"; -import {ErrorWithCause} from "pony-cause"; import {childLogger, LogData, LogDataPretty} from "@foxxmd/logging"; import {PassThrough} from "node:stream"; import {Logger} from '@foxxmd/logging'; @@ -85,13 +84,13 @@ export const initServer = async (parentLogger: Logger, appLoggerStream: PassThro logger.info(`User-defined base URL for UI and redirect URLs (spotify, deezer, lastfm): ${local}`) } }).on('error', (err) => { - throw new ErrorWithCause('Server encountered unrecoverable error', {cause: err}); + throw new Error('Server encountered unrecoverable error', {cause: err}); }); } catch (e) { - throw new ErrorWithCause('Server encountered unrecoverable error', {cause: e}); + throw new Error('Server encountered unrecoverable error', {cause: e}); } } catch (e) { - throw new ErrorWithCause('Server crashed with uncaught exception', {cause: e}); + throw new Error('Server crashed with uncaught exception', {cause: e}); } } diff --git a/src/backend/sources/AbstractSource.ts b/src/backend/sources/AbstractSource.ts index 0c9b837c..915f16e6 100644 --- a/src/backend/sources/AbstractSource.ts +++ b/src/backend/sources/AbstractSource.ts @@ -36,7 +36,6 @@ import TupleMap from "../common/TupleMap.js"; import { PlayObject, TA_CLOSE } from "../../core/Atomic.js"; import { buildTrackString, capitalize } from "../../core/StringUtils.js"; import { isNodeNetworkException } from "../common/errors/NodeErrors.js"; -import {ErrorWithCause} from "pony-cause"; import { comparePlayTemporally, temporalAccuracyIsAtLeast } from "../utils/TimeUtils.js"; export interface RecentlyPlayedOptions { @@ -111,7 +110,7 @@ export default abstract class AbstractSource implements Authenticatable { this.logger.info('Fully Initialized!'); return true; } catch(e) { - this.logger.error(new ErrorWithCause('Initialization failed', {cause: e})); + this.logger.error(new Error('Initialization failed', {cause: e})); return false; } } @@ -135,7 +134,7 @@ export default abstract class AbstractSource implements Authenticatable { this.buildOK = true; } catch (e) { this.buildOK = false; - throw new ErrorWithCause('Building required data for initialization failed', {cause: e}); + throw new Error('Building required data for initialization failed', {cause: e}); } } @@ -166,7 +165,7 @@ export default abstract class AbstractSource implements Authenticatable { this.connectionOK = true; } catch (e) { this.connectionOK = false; - throw new ErrorWithCause('Communicating with upstream service failed', {cause: e}); + throw new Error('Communicating with upstream service failed', {cause: e}); } } @@ -202,7 +201,7 @@ export default abstract class AbstractSource implements Authenticatable { // only signal as auth failure if error was NOT a node network error this.authFailure = findCauseByFunc(e, isNodeNetworkException) === undefined; this.authed = false; - throw new ErrorWithCause(`Authentication test failed!${this.authFailure === false ? ' Due to a network issue. Will retry authentication on next heartbeat.' : ''}`, {cause: e}) + throw new Error(`Authentication test failed!${this.authFailure === false ? ' Due to a network issue. Will retry authentication on next heartbeat.' : ''}`, {cause: e}) } } @@ -322,7 +321,7 @@ export default abstract class AbstractSource implements Authenticatable { try { backlogPlays = await this.getBackloggedPlays(); } catch (e) { - throw new ErrorWithCause('Error occurred while fetching backlogged plays', {cause: e}); + throw new Error('Error occurred while fetching backlogged plays', {cause: e}); } const discovered = this.discover(backlogPlays); @@ -385,7 +384,7 @@ export default abstract class AbstractSource implements Authenticatable { try { await this.processBacklog(); } catch (e) { - this.logger.error(new ErrorWithCause('Cannot start polling because error occurred while processing backlog', {cause: e})); + this.logger.error(new Error('Cannot start polling because error occurred while processing backlog', {cause: e})); this.notify({ title: `${this.identifier} - Polling Error`, message: 'Cannot start polling because error occurred while processing backlog.', diff --git a/src/backend/sources/ChromecastSource.ts b/src/backend/sources/ChromecastSource.ts index c5cef8c6..c0f08dd2 100644 --- a/src/backend/sources/ChromecastSource.ts +++ b/src/backend/sources/ChromecastSource.ts @@ -11,7 +11,7 @@ import { import {EventEmitter} from "events"; import {MediaController, PersistentClient, Media, createPlatform} from "@foxxmd/chromecast-client"; import {Client as CastClient} from 'castv2'; -import {ErrorWithCause, findCauseByReference} from "pony-cause"; +import {findCauseByReference} from "pony-cause"; import { PlayObject } from "../../core/Atomic.js"; import dayjs from "dayjs"; import { RecentlyPlayedOptions } from "./AbstractSource.js"; @@ -140,18 +140,18 @@ export class ChromecastSource extends MemorySource { for (const device of devices) { this.initializeDevice({name: device.name, addresses: [device.address], type: 'googlecast'}).catch((err) => { - this.logger.error(new ErrorWithCause('Uncaught error occurred while connecting to manually configured device', {cause: err})); + this.logger.error(new Error('Uncaught error occurred while connecting to manually configured device', {cause: err})); }); } if (useAutoDiscovery) { if (useAvahi) { this.discoverAvahi(initial).catch((err) => { - this.logger.error(new ErrorWithCause('Uncaught error occurred during mDNS discovery via Avahi', {cause: err})); + this.logger.error(new Error('Uncaught error occurred during mDNS discovery via Avahi', {cause: err})); }); } else { this.discoverNative(initial).catch((err) => { - this.logger.error(new ErrorWithCause('Uncaught error occurred during mDNS discovery', {cause: err})); + this.logger.error(new Error('Uncaught error occurred during mDNS discovery', {cause: err})); }); } } @@ -167,7 +167,7 @@ export class ChromecastSource extends MemorySource { }, }); } catch (e) { - this.logger.error(new ErrorWithCause('Uncaught error occurred during mDNS discovery via Avahi', {cause: e})); + this.logger.error(new Error('Uncaught error occurred during mDNS discovery via Avahi', {cause: e})); } } @@ -181,7 +181,7 @@ export class ChromecastSource extends MemorySource { }, }); } catch (e) { - this.logger.error(new ErrorWithCause('Uncaught error occurred during mDNS discovery', {cause: e})); + this.logger.error(new Error('Uncaught error occurred during mDNS discovery', {cause: e})); } } @@ -261,10 +261,10 @@ export class ChromecastSource extends MemorySource { await client.connect(); } catch (e) { if(index < device.addresses.length - 1) { - this.logger.warn(new ErrorWithCause(`Could not connect to ${device.name} but more interfaces exist, will attempt next host.`, {cause: e})); + this.logger.warn(new Error(`Could not connect to ${device.name} but more interfaces exist, will attempt next host.`, {cause: e})); continue; } else { - throw new ErrorWithCause(`Could not connect to ${device.name} and no additional interfaces exist`, {cause: e}); + throw new Error(`Could not connect to ${device.name} and no additional interfaces exist`, {cause: e}); } } @@ -284,7 +284,7 @@ export class ChromecastSource extends MemorySource { } if(event === "reconnect") { if(payload instanceof Error) { - info.logger.warn(new ErrorWithCause(`Failed to reconnect, will retry ${5 - info.retries} more times`, {cause: e})) + info.logger.warn(new Error(`Failed to reconnect, will retry ${5 - info.retries} more times`, {cause: e})) } else { info.logger.verbose(`Reconnected`); info.retries = 0; @@ -312,13 +312,13 @@ export class ChromecastSource extends MemorySource { break; case 'error': if(info === undefined) { - this.logger.error(new ErrorWithCause(`(${clientName}) Encountered error in castv2 lib`, {cause: payload as Error})); + this.logger.error(new Error(`(${clientName}) Encountered error in castv2 lib`, {cause: payload as Error})); } else { if(NETWORK_ERROR_FAILURE_CODES.some(x => (payload as Error).message.includes(x))) { - info.logger.warn(new ErrorWithCause(`Encountered network error. Will try to reconnect to device`, {cause: payload as Error})); + info.logger.warn(new Error(`Encountered network error. Will try to reconnect to device`, {cause: payload as Error})); info.client.client.close(); } else { - info.logger.error(new ErrorWithCause(`Encountered error in castv2 lib`, {cause: payload as Error})); + info.logger.error(new Error(`Encountered error in castv2 lib`, {cause: payload as Error})); } } break; @@ -336,7 +336,7 @@ export class ChromecastSource extends MemorySource { apps = await getCurrentPlatformApplications(v.platform); v.retries = 0; } catch (e) { - v.logger.warn(new ErrorWithCause(`Could not refresh applications. Will remove after ${5 - v.retries} retries if error does not resolve itself.`, {cause: e})); + v.logger.warn(new Error(`Could not refresh applications. Will remove after ${5 - v.retries} retries if error does not resolve itself.`, {cause: e})); const validationError = findCauseByReference(e, ContextualValidationError); if(validationError && validationError.data !== undefined) { v.logger.warn(JSON.stringify(validationError.data)); @@ -495,7 +495,7 @@ export class ChromecastSource extends MemorySource { try { await this.refreshApplications(); } catch (e) { - this.logger.warn(new ErrorWithCause('Could not refresh all applications', {cause: e})); + this.logger.warn(new Error('Could not refresh all applications', {cause: e})); } for (const [k, v] of this.devices.entries()) { @@ -629,7 +629,7 @@ export class ChromecastSource extends MemorySource { plays.push(playerState); } catch (e) { - application.logger.warn(new ErrorWithCause(`Could not get Player State`, {cause: e})) + application.logger.warn(new Error(`Could not get Player State`, {cause: e})) const validationError = findCauseByReference(e, ContextualValidationError); if (validationError && validationError.data !== undefined) { application.logger.warn(JSON.stringify(validationError.data)); diff --git a/src/backend/sources/DeezerSource.ts b/src/backend/sources/DeezerSource.ts index 9aa941a0..22c3d8db 100644 --- a/src/backend/sources/DeezerSource.ts +++ b/src/backend/sources/DeezerSource.ts @@ -14,7 +14,6 @@ import { DeezerSourceConfig } from "../common/infrastructure/config/source/deeze import { DEFAULT_RETRY_MULTIPLIER, FormatPlayObjectOptions, InternalConfig } from "../common/infrastructure/Atomic.js"; import EventEmitter from "events"; import { PlayObject } from "../../core/Atomic.js"; -import {ErrorWithCause} from "pony-cause"; export default class DeezerSource extends AbstractSource { workingCredsPath; @@ -102,7 +101,7 @@ export default class DeezerSource extends AbstractSource { this.logger.warn(`No Deezer credentials file found at ${this.workingCredsPath}`); } } catch (e) { - throw new ErrorWithCause('Current deezer credentials file exists but could not be parsed', {cause: e}); + throw new Error('Current deezer credentials file exists but could not be parsed', {cause: e}); } if (this.config.data.accessToken === undefined) { if (this.config.data.clientId === undefined) { diff --git a/src/backend/sources/LastfmSource.ts b/src/backend/sources/LastfmSource.ts index 81a0865a..9dc631a1 100644 --- a/src/backend/sources/LastfmSource.ts +++ b/src/backend/sources/LastfmSource.ts @@ -9,8 +9,7 @@ import MemorySource from "./MemorySource.js"; import { LastfmSourceConfig } from "../common/infrastructure/config/source/lastfm.js"; import dayjs from "dayjs"; import { isNodeNetworkException } from "../common/errors/NodeErrors.js"; -import {ErrorWithCause} from "pony-cause"; -import request, {options} from "superagent"; +import request from "superagent"; export default class LastfmSource extends MemorySource { @@ -57,9 +56,9 @@ export default class LastfmSource extends MemorySource { return true; } catch (e) { if(isNodeNetworkException(e)) { - throw new ErrorWithCause('Could not communicate with Last.fm API server', {cause: e}); + throw new Error('Could not communicate with Last.fm API server', {cause: e}); } else if(e.status >= 500) { - throw new ErrorWithCause('Last.fm API server returning an unexpected response', {cause: e}) + throw new Error('Last.fm API server returning an unexpected response', {cause: e}) } return true; } diff --git a/src/backend/sources/ListenbrainzSource.ts b/src/backend/sources/ListenbrainzSource.ts index 9566c156..0ef9b53e 100644 --- a/src/backend/sources/ListenbrainzSource.ts +++ b/src/backend/sources/ListenbrainzSource.ts @@ -4,7 +4,6 @@ import EventEmitter from "events"; import { ListenBrainzSourceConfig } from "../common/infrastructure/config/source/listenbrainz.js"; import { ListenbrainzApiClient } from "../common/vendor/ListenbrainzApiClient.js"; import MemorySource from "./MemorySource.js"; -import {ErrorWithCause} from "pony-cause"; import request from "superagent"; import {isNodeNetworkException} from "../common/errors/NodeErrors.js"; import {PlayObject, SOURCE_SOT} from "../../core/Atomic.js"; @@ -42,9 +41,9 @@ export default class ListenbrainzSource extends MemorySource { return true; } catch (e) { if(isNodeNetworkException(e)) { - throw new ErrorWithCause('Could not communicate with Listenbrainz API server', {cause: e}); + throw new Error('Could not communicate with Listenbrainz API server', {cause: e}); } else if(e.status !== 410) { - throw new ErrorWithCause('Listenbrainz API server returning an unexpected response', {cause: e}) + throw new Error('Listenbrainz API server returning an unexpected response', {cause: e}) } return true; } @@ -58,7 +57,7 @@ export default class ListenbrainzSource extends MemorySource { return await this.api.testAuth(); } catch (e) { throw e; - //throw new ErrorWithCause('Could not communicate with Listenbrainz API', {cause: e}); + //throw new Error('Could not communicate with Listenbrainz API', {cause: e}); } } diff --git a/src/backend/sources/MPRISSource.ts b/src/backend/sources/MPRISSource.ts index dff939b2..0ace261c 100644 --- a/src/backend/sources/MPRISSource.ts +++ b/src/backend/sources/MPRISSource.ts @@ -13,7 +13,6 @@ import MemorySource from "./MemorySource.js"; import { RecentlyPlayedOptions } from "./AbstractSource.js"; import { removeDuplicates } from "../utils.js"; import EventEmitter from "events"; -import {ErrorWithCause} from "pony-cause"; import { PlayObject } from "../../core/Atomic.js"; import {DBusInterface, sessionBus} from 'dbus-ts'; import { Interfaces as Notifications } from '@dbus-types/notifications' @@ -96,7 +95,7 @@ export class MPRISSource extends MemorySource { await this.getDBus(); return true; } catch (e) { - throw new ErrorWithCause('Could not get DBus interface from operating system', {cause: e}); + throw new Error('Could not get DBus interface from operating system', {cause: e}); } } @@ -144,7 +143,7 @@ export class MPRISSource extends MemorySource { }); } catch (e) { - this.logger.warn(new ErrorWithCause(`Could not parse D-bus info for player ${plainPlayerName}`, {cause: e})); + this.logger.warn(new Error(`Could not parse D-bus info for player ${plainPlayerName}`, {cause: e})); } } @@ -158,7 +157,7 @@ export class MPRISSource extends MemorySource { // microseconds return dayjs.duration({milliseconds: Number(pos / 1000)}).asSeconds(); } catch(e) { - throw new ErrorWithCause('Could not get player Position', {cause: e}); + throw new Error('Could not get player Position', {cause: e}); } } @@ -167,7 +166,7 @@ export class MPRISSource extends MemorySource { const status = await props['PlaybackStatus']; return status as PlaybackStatus; } catch (e) { - throw new ErrorWithCause('Could not get player PlaybackStatus', {cause: e}) + throw new Error('Could not get player PlaybackStatus', {cause: e}) } } @@ -176,7 +175,7 @@ export class MPRISSource extends MemorySource { const metadata = await props['Metadata']; return this.metadataToPlain(metadata); } catch(e) { - throw new ErrorWithCause('Could not get player Metadata', {cause: e}); + throw new Error('Could not get player Metadata', {cause: e}); } } diff --git a/src/backend/sources/MopidySource.ts b/src/backend/sources/MopidySource.ts index 932e4d48..0215e8ee 100644 --- a/src/backend/sources/MopidySource.ts +++ b/src/backend/sources/MopidySource.ts @@ -15,7 +15,6 @@ import pEvent from 'p-event'; import { RecentlyPlayedOptions } from "./AbstractSource.js"; import { PlayObject } from "../../core/Atomic.js"; import { buildTrackString } from "../../core/StringUtils.js"; -import {ErrorWithCause} from "pony-cause"; import {loggerTest} from "@foxxmd/logging"; export class MopidySource extends MemorySource { @@ -118,7 +117,7 @@ export class MopidySource extends MemorySource { return true; } else { this.client.close(); - throw new ErrorWithCause(`Could not connect to Mopidy server`, {cause: (res as Error)}); + throw new Error(`Could not connect to Mopidy server`, {cause: (res as Error)}); } } diff --git a/src/backend/sources/SpotifySource.ts b/src/backend/sources/SpotifySource.ts index dd4098e5..acac7c9e 100644 --- a/src/backend/sources/SpotifySource.ts +++ b/src/backend/sources/SpotifySource.ts @@ -30,7 +30,6 @@ import ArtistObjectSimplified = SpotifyApi.ArtistObjectSimplified; import AlbumObjectSimplified = SpotifyApi.AlbumObjectSimplified; import UserDevice = SpotifyApi.UserDevice; import MemorySource from "./MemorySource.js"; -import {ErrorWithCause} from "pony-cause"; import { PlayObject, SCROBBLE_TS_SOC_END, SCROBBLE_TS_SOC_START, ScrobbleTsSOC } from "../../core/Atomic.js"; import { buildTrackString, truncateStringToLength } from "../../core/StringUtils.js"; import { isNodeNetworkException } from "../common/errors/NodeErrors.js"; @@ -256,10 +255,10 @@ export default class SpotifySource extends MemorySource { return true; } catch (e) { if(isNodeNetworkException(e)) { - throw new ErrorWithCause('Could not communicate with Spotify API server', {cause: e}); + throw new Error('Could not communicate with Spotify API server', {cause: e}); } if(e.status >= 500) { - throw new ErrorWithCause('Spotify API server returned an unexpected response', { cause: e}); + throw new Error('Spotify API server returned an unexpected response', { cause: e}); } return true; } @@ -277,9 +276,6 @@ export default class SpotifySource extends MemorySource { if(isNodeNetworkException(e)) { this.logger.error('Could not communicate with Spotify API'); } - // this.authFailure = !(e instanceof ErrorWithCause && e.cause !== undefined && isNodeNetworkException(e.cause)); - // this.logger.error(new ErrorWithCause('Could not successfully communicate with Spotify API', {cause: e})); - // this.authed = false; throw e; } } @@ -405,7 +401,7 @@ export default class SpotifySource extends MemorySource { if(hasApiError(e)) { throw new UpstreamError('Error occurred while trying to retrieve current playback state', {cause: e}); } - throw new ErrorWithCause('Error occurred while trying to retrieve current playback state', {cause: e}); + throw new Error('Error occurred while trying to retrieve current playback state', {cause: e}); } } diff --git a/src/backend/sources/SubsonicSource.ts b/src/backend/sources/SubsonicSource.ts index b08f1e19..bee2dc42 100644 --- a/src/backend/sources/SubsonicSource.ts +++ b/src/backend/sources/SubsonicSource.ts @@ -10,11 +10,8 @@ import { RecentlyPlayedOptions } from "./AbstractSource.js"; import EventEmitter from "events"; import { PlayObject } from "../../core/Atomic.js"; import {isNodeNetworkException} from "../common/errors/NodeErrors.js"; -import {ErrorWithCause} from "pony-cause"; import {UpstreamError} from "../common/errors/UpstreamError.js"; import {getSubsonicResponse, SubsonicResponse, SubsonicResponseCommon} from "../common/vendor/subsonic/interfaces.js"; -import {hash} from "@astronautlabs/mdns/dist/hash.js"; -import e from "express"; dayjs.extend(isSameOrAfter); @@ -216,7 +213,7 @@ export class SubsonicSource extends MemorySource { } else if(e.status >= 500) { throw new UpstreamError('Subsonic server returning an unexpected response', {cause: e}) } else { - throw new ErrorWithCause('Unexpected error occurred', {cause: e}) + throw new Error('Unexpected error occurred', {cause: e}) } } } diff --git a/src/backend/utils.ts b/src/backend/utils.ts index 724298c4..c15be4bf 100644 --- a/src/backend/utils.ts +++ b/src/backend/utils.ts @@ -21,7 +21,7 @@ import { } from "./common/infrastructure/Atomic.js"; import {Request} from "express"; import pathUtil from "path"; -import {ErrorWithCause, getErrorCause} from "pony-cause"; +import {getErrorCause} from "pony-cause"; import backoffStrategies from '@kenyip/backoff-strategies'; import {replaceResultTransformer, stripIndentTransformer, TemplateTag, trimResultTransformer} from 'common-tags'; import {Duration} from "dayjs/plugin/duration.js"; @@ -40,12 +40,12 @@ export async function readJson(this: any, path: any, {throwOnNotFound = true} = const {code} = e; if (code === 'ENOENT') { if (throwOnNotFound) { - throw new ErrorWithCause(`No file found at given path: ${path}`, {cause: e}); + throw new Error(`No file found at given path: ${path}`, {cause: e}); } else { return; } } - throw new ErrorWithCause(`Encountered error while parsing file: ${path}`, {cause: e}) + throw new Error(`Encountered error while parsing file: ${path}`, {cause: e}) } } @@ -536,13 +536,13 @@ export const fileOrDirectoryIsWriteable = (location: string) => { // also can't access directory :( throw new Error(`No ${isDir ? 'directory' : 'file'} exists at ${location} and application does not have permission to write to the parent directory`); } else { - throw new ErrorWithCause(`No ${isDir ? 'directory' : 'file'} exists at ${location} and application is unable to access the parent directory due to a system error`, {cause: accessError}); + throw new Error(`No ${isDir ? 'directory' : 'file'} exists at ${location} and application is unable to access the parent directory due to a system error`, {cause: accessError}); } } } else if(code === 'EACCES') { throw new Error(`${isDir ? 'Directory' : 'File'} exists at ${location} but application does not have permission to write to it.`); } else { - throw new ErrorWithCause(`${isDir ? 'Directory' : 'File'} exists at ${location} but application is unable to access it due to a system error`, {cause: err}); + throw new Error(`${isDir ? 'Directory' : 'File'} exists at ${location} but application is unable to access it due to a system error`, {cause: err}); } } } @@ -600,7 +600,7 @@ export const parseRegexSingleOrFail = (reg: RegExp, val: string): RegExResult | const results = parseRegex(reg, val); if (results !== undefined) { if (results.length > 1) { - throw new ErrorWithCause(`Expected Regex to match once but got ${results.length} results. Either Regex must NOT be global (using 'g' flag) or parsed value must only match regex once. Given: ${val} || Regex: ${reg.toString()}`); + throw new Error(`Expected Regex to match once but got ${results.length} results. Either Regex must NOT be global (using 'g' flag) or parsed value must only match regex once. Given: ${val} || Regex: ${reg.toString()}`); } return results[0]; } @@ -733,7 +733,7 @@ export const getAddress = (host = '0.0.0.0', logger?: Logger): { v4?: string, v6 } catch (e) { if (process.env.DEBUG_MODE === 'true') { if (logger !== undefined) { - logger.warn(new ErrorWithCause('Could not get machine IP address', {cause: e})); + logger.warn(new Error('Could not get machine IP address', {cause: e})); } else { console.warn('Could not get machine IP address'); console.warn(e); diff --git a/src/backend/utils/MDNSUtils.ts b/src/backend/utils/MDNSUtils.ts index 6d25c332..baa31314 100644 --- a/src/backend/utils/MDNSUtils.ts +++ b/src/backend/utils/MDNSUtils.ts @@ -2,7 +2,6 @@ import {Logger} from "@foxxmd/logging"; import AvahiBrowser from 'avahi-browse'; import { MaybeLogger } from "../common/logging.js"; import { sleep } from "../utils.js"; -import {ErrorWithCause} from "pony-cause"; import { MdnsDeviceInfo } from "../common/infrastructure/Atomic.js"; import {Browser, Service, ServiceType} from "@astronautlabs/mdns"; import {debounce, DebouncedFunction} from "./debounce.js"; @@ -78,7 +77,7 @@ export const discoveryAvahi = async (service: string, options?: DiscoveryOptions } }); browser.on(AvahiBrowser.EVENT_DNSSD_ERROR, (err) => { - const e = new ErrorWithCause('Error occurred while using avahi-browse', {cause: err}); + const e = new Error('Error occurred while using avahi-browse', {cause: err}); if (onDnsError) { onDnsError(e) } else { @@ -97,7 +96,7 @@ export const discoveryAvahi = async (service: string, options?: DiscoveryOptions } maybeLogger.debug('Stopped discovery'); } catch (e) { - maybeLogger.warn(new ErrorWithCause('mDNS device discovery with avahi-browse failed', {cause: e})); + maybeLogger.warn(new Error('mDNS device discovery with avahi-browse failed', {cause: e})); } } @@ -121,7 +120,7 @@ export const discoveryNative = async (service: string, options?: DiscoveryOption }) .start(); testBrowser.on('error', (err) => { - maybeLogger.error(new ErrorWithCause('Error occurred during mDNS service discovery', {cause: err})); + maybeLogger.error(new Error('Error occurred during mDNS service discovery', {cause: err})); }); maybeLogger.debug('Waiting 1s to gather advertised mdns services...'); await sleep(1000); @@ -141,7 +140,7 @@ export const discoveryNative = async (service: string, options?: DiscoveryOption } }) browser.on('error', (err) => { - const e = new ErrorWithCause('Error occurred during mDNS discovery', {cause: err}); + const e = new Error('Error occurred during mDNS discovery', {cause: err}); if (onDnsError) { onDnsError(e) } else { -- 2.51.2