From 21415dcff368e9827b6ebed20569a3aa5ae3b4a7 Mon Sep 17 00:00:00 2001 From: orangecoding Date: Sat, 13 Sep 2025 18:57:56 +0200 Subject: [PATCH] using winston logger --- index.js | 11 +- lib/FredyRuntime.js | 9 +- lib/api/api.js | 4 +- lib/api/routes/generalSettingsRoute.js | 3 +- lib/api/routes/jobRouter.js | 7 +- lib/api/routes/loginRoute.js | 3 +- lib/notification/adapter/mailJet.js | 5 +- lib/provider/immoscout.js | 3 +- lib/services/demoCleanup.js | 3 +- lib/services/extractor/extractor.js | 3 +- lib/services/extractor/parser/parser.js | 15 +- lib/services/extractor/puppeteerExtractor.js | 5 +- lib/services/extractor/utils.js | 6 +- lib/services/logger.js | 15 ++ lib/services/storage/jobStorage.js | 5 +- lib/services/tracking/Tracker.js | 7 +- lib/utils.js | 6 +- package.json | 3 +- yarn.lock | 166 ++++++++++++++++++- 19 files changed, 230 insertions(+), 49 deletions(-) create mode 100644 lib/services/logger.js diff --git a/index.js b/index.js index 6d833b4..446cd18 100755 --- a/index.js +++ b/index.js @@ -9,6 +9,7 @@ import './lib/api/api.js'; import { handleDemoUser } from './lib/services/storage/userStorage.js'; import { cleanupDemoAtMidnight } from './lib/services/demoCleanup.js'; import { initTrackerCron } from './lib/services/tracking/Tracker-Cron.js'; +import logger from './lib/services/logger.js'; //if db folder does not exist, ensure to create it before loading anything else if (!fs.existsSync('./db')) { fs.mkdirSync('./db'); @@ -17,13 +18,11 @@ const path = './lib/provider'; const provider = fs.readdirSync(path).filter((file) => file.endsWith('.js')); //assuming interval is always in minutes const INTERVAL = config.interval * 60 * 1000; -/* eslint-disable no-console */ -console.log(`Started Fredy successfully. Ui can be accessed via http://localhost:${config.port}`); +logger.info(`Started Fredy successfully. Ui can be accessed via http://localhost:${config.port}`); if (config.demoMode) { - console.info('Running in demo mode'); + logger.info('Running in demo mode'); cleanupDemoAtMidnight(); } -/* eslint-enable no-console */ const fetchedProvider = await Promise.all( provider.filter((provider) => provider.endsWith('.js')).map(async (pro) => import(`${path}/${pro}`)), ); @@ -51,9 +50,7 @@ setInterval( }); }); } else { - /* eslint-disable no-console */ - console.debug('Working hours set. Skipping as outside of working hours.'); - /* eslint-enable no-console */ + logger.debug('Working hours set. Skipping as outside of working hours.'); } } return exec; diff --git a/lib/FredyRuntime.js b/lib/FredyRuntime.js index d73494c..5097199 100755 --- a/lib/FredyRuntime.js +++ b/lib/FredyRuntime.js @@ -3,6 +3,7 @@ import { setKnownListings, getKnownListings } from './services/storage/listingsS import * as notify from './notification/notify.js'; import Extractor from './services/extractor/extractor.js'; import urlModifier from './services/queryStringMutator.js'; +import logger from './services/logger.js'; class FredyRuntime { /** @@ -59,7 +60,7 @@ class FredyRuntime { }) .catch((err) => { reject(err); - console.error(err); + logger.error(err); }); }); } @@ -104,9 +105,7 @@ class FredyRuntime { const filteredList = listings.filter((listing) => { const similar = this._similarityCache.hasSimilarEntries(listing.title, listing.address); if (similar) { - /* eslint-disable no-console */ - console.debug(`Filtering similar entry for title: ${listing.title} and address ${listing.address}`); - /* eslint-enable no-console */ + logger.debug(`Filtering similar entry for title: ${listing.title} and address ${listing.address}`); } return !similar; }); @@ -115,7 +114,7 @@ class FredyRuntime { } _handleError(err) { - if (err.name !== 'NoNewListingsWarning') console.error(err); + if (err.name !== 'NoNewListingsWarning') logger.error(err); } } diff --git a/lib/api/api.js b/lib/api/api.js index ca2cb8f..194855c 100644 --- a/lib/api/api.js +++ b/lib/api/api.js @@ -13,6 +13,7 @@ import files from 'serve-static'; import path from 'path'; import { getDirName } from '../utils.js'; import { demoRouter } from './routes/demoRouter.js'; +import logger from '../services/logger.js'; const service = restana(); const staticService = files(path.join(getDirName(), '../ui/public')); const PORT = config.port || 9998; @@ -34,7 +35,6 @@ service.use('/api/login', loginRouter); //this route is unsecured intentionally as it is being queried from the login page service.use('/api/demo', demoRouter); -/* eslint-disable no-console */ service.start(PORT).then(() => { - console.info(`Started API service on port ${PORT}`); + logger.debug(`Started API service on port ${PORT}`); }); diff --git a/lib/api/routes/generalSettingsRoute.js b/lib/api/routes/generalSettingsRoute.js index f6acbde..bff4eac 100644 --- a/lib/api/routes/generalSettingsRoute.js +++ b/lib/api/routes/generalSettingsRoute.js @@ -2,6 +2,7 @@ import restana from 'restana'; import { config, getDirName, readConfigFromStorage, refreshConfig } from '../../utils.js'; import fs from 'fs'; import { handleDemoUser } from '../../services/storage/userStorage.js'; +import logger from '../../services/logger.js'; const service = restana(); const generalSettingsRouter = service.newRouter(); generalSettingsRouter.get('/', async (req, res) => { @@ -20,7 +21,7 @@ generalSettingsRouter.post('/', async (req, res) => { await refreshConfig(); handleDemoUser(); } catch (err) { - console.error(err); + logger.error(err); res.send(new Error('Error while trying to write settings.')); return; } diff --git a/lib/api/routes/jobRouter.js b/lib/api/routes/jobRouter.js index f906444..5d47334 100644 --- a/lib/api/routes/jobRouter.js +++ b/lib/api/routes/jobRouter.js @@ -3,6 +3,7 @@ import * as jobStorage from '../../services/storage/jobStorage.js'; import * as userStorage from '../../services/storage/userStorage.js'; import { config } from '../../utils.js'; import { isAdmin } from '../security.js'; +import logger from '../../services/logger.js'; const service = restana(); const jobRouter = service.newRouter(); function doesJobBelongsToUser(job, req) { @@ -43,7 +44,7 @@ jobRouter.post('/', async (req, res) => { }); } catch (error) { res.send(new Error(error)); - console.error(error); + logger.error(error); } res.send(); }); @@ -58,7 +59,7 @@ jobRouter.delete('', async (req, res) => { } } catch (error) { res.send(new Error(error)); - console.error(error); + logger.error(error); } res.send(); }); @@ -77,7 +78,7 @@ jobRouter.put('/:jobId/status', async (req, res) => { } } catch (error) { res.send(new Error(error)); - console.error(error); + logger.error(error); } res.send(); }); diff --git a/lib/api/routes/loginRoute.js b/lib/api/routes/loginRoute.js index 061b274..1eb28cc 100644 --- a/lib/api/routes/loginRoute.js +++ b/lib/api/routes/loginRoute.js @@ -3,6 +3,7 @@ import * as userStorage from '../../services/storage/userStorage.js'; import * as hasher from '../../services/security/hash.js'; import { config } from '../../utils.js'; import { trackDemoAccessed } from '../../services/tracking/Tracker.js'; +import logger from '../../services/logger.js'; const service = restana(); const loginRouter = service.newRouter(); loginRouter.get('/user', async (req, res) => { @@ -35,7 +36,7 @@ loginRouter.post('/', async (req, res) => { res.send(200); return; } else { - console.error(`User ${username} tried to login, but password was wrong.`); + logger.error(`User ${username} tried to login, but password was wrong.`); } res.send(401); }); diff --git a/lib/notification/adapter/mailJet.js b/lib/notification/adapter/mailJet.js index 826e913..7c57845 100755 --- a/lib/notification/adapter/mailJet.js +++ b/lib/notification/adapter/mailJet.js @@ -5,6 +5,7 @@ import Handlebars from 'handlebars'; import fetch from 'node-fetch'; import { markdown2Html } from '../../services/markdown.js'; import { getDirName, normalizeImageUrl } from '../../utils.js'; +import logger from '../../services/logger.js'; const __dirname = getDirName(); const template = fs.readFileSync(path.resolve(__dirname + '/notification/emailTemplate/template.hbs'), 'utf8'); @@ -24,7 +25,7 @@ const toBase64 = async (url) => { const ab = await res.arrayBuffer(); return Buffer.from(ab).toString('base64'); } catch (error) { - console.error(`Error fetching image from ${url}:`, error.message); + logger.error(`Error fetching image from ${url}:`, error.message); throw error; } }; @@ -62,7 +63,7 @@ const mapListingsWithCid = async (serviceName, jobKey, listings) => { item.hasImage = true; item.imageCid = cid; } catch (error) { - console.warn(`Skipping image for listing ${i} due to error: ${error.message}`); + logger.warn(`Skipping image for listing ${i} due to error: ${error.message}`); } } diff --git a/lib/provider/immoscout.js b/lib/provider/immoscout.js index 06a4b39..9dac88b 100644 --- a/lib/provider/immoscout.js +++ b/lib/provider/immoscout.js @@ -37,6 +37,7 @@ import utils, { buildHash } from '../utils.js'; import { convertWebToMobile } from '../services/immoscout/immoscout-web-translator.js'; +import logger from '../services/logger.js'; let appliedBlackList = []; async function getListings(url) { @@ -52,7 +53,7 @@ async function getListings(url) { }), }); if (!response.ok) { - console.error('Error fetching data from ImmoScout Mobile API:', response.statusText); + logger.error('Error fetching data from ImmoScout Mobile API:', response.statusText); return []; } diff --git a/lib/services/demoCleanup.js b/lib/services/demoCleanup.js index 4d995a4..de1899f 100644 --- a/lib/services/demoCleanup.js +++ b/lib/services/demoCleanup.js @@ -2,6 +2,7 @@ import { setInterval } from 'node:timers'; import { removeJobsByUserName } from './storage/jobStorage.js'; import { config } from '../utils.js'; import { getUsers } from './storage/userStorage.js'; +import logger from './logger.js'; /** * if we are running in demo environment, we have to cleanup the db files (specifically the jobs table) @@ -29,7 +30,7 @@ function cleanup() { if (config.demoMode) { const demoUser = getUsers(false).find((user) => user.username === 'demo'); if (demoUser == null) { - console.error('Demo user not found, cannot remove Jobs'); + logger.error('Demo user not found, cannot remove Jobs'); return; } removeJobsByUserName(demoUser.id); diff --git a/lib/services/extractor/extractor.js b/lib/services/extractor/extractor.js index 8e5cbaf..b881141 100644 --- a/lib/services/extractor/extractor.js +++ b/lib/services/extractor/extractor.js @@ -1,6 +1,7 @@ import { setDebug } from './utils.js'; import puppeteerExtractor from './puppeteerExtractor.js'; import { loadParser, parse } from './parser/parser.js'; +import logger from '../logger.js'; const DEFAULT_OPTIONS = { debug: false, @@ -32,7 +33,7 @@ export default class Extractor { loadParser(this.responseText); } } catch (error) { - console.error('Error trying to load page.', error); + logger.error('Error trying to load page.', error); } return this; }; diff --git a/lib/services/extractor/parser/parser.js b/lib/services/extractor/parser/parser.js index 3507250..84b0e69 100644 --- a/lib/services/extractor/parser/parser.js +++ b/lib/services/extractor/parser/parser.js @@ -1,4 +1,5 @@ import * as cheerio from 'cheerio'; +import logger from '../../logger.js'; let $ = null; @@ -8,19 +9,19 @@ export function loadParser(text) { export function parse(crawlContainer, crawlFields, text, url) { if (!text) { - console.warn('No content found for ', url); + logger.warn('No content found for ', url); return null; } if (!crawlContainer || !crawlFields) { - console.warn('Cannot parse, selector was empty for url ', url); + logger.warn('Cannot parse, selector was empty for url ', url); return null; } const result = []; if ($(crawlContainer).length === 0) { - console.warn('No elements in crawl container found for url ', url); + logger.warn('No elements in crawl container found for url ', url); return null; } @@ -58,7 +59,7 @@ export function parse(crawlContainer, crawlFields, text, url) { parsedObject[key] = value || null; } catch (error) { - console.error(`Error parsing field '${key}' with selector '${fieldSelector}':`, error); + logger.error(`Error parsing field '${key}' with selector '${fieldSelector}':`, error); parsedObject[key] = null; } } @@ -66,9 +67,7 @@ export function parse(crawlContainer, crawlFields, text, url) { if (parsedObject.id != null) { result.push(parsedObject); } else { - /* eslint-disable no-console */ - console.debug('ID not found. Not relaying object.'); - /* eslint-enable no-console */ + logger.debug('ID not found. Not relaying object.'); } }); @@ -91,7 +90,7 @@ function applyModifiers(value, modifiers) { value = value.replace(/\n/g, ' '); break; default: - console.warn(`Unknown modifier: ${modifier}`); + logger.warn(`Unknown modifier: ${modifier}`); } }); diff --git a/lib/services/extractor/puppeteerExtractor.js b/lib/services/extractor/puppeteerExtractor.js index 7d858de..b8f7259 100644 --- a/lib/services/extractor/puppeteerExtractor.js +++ b/lib/services/extractor/puppeteerExtractor.js @@ -1,6 +1,7 @@ import puppeteer from 'puppeteer-extra'; import StealthPlugin from 'puppeteer-extra-plugin-stealth'; import { debug, DEFAULT_HEADER, botDetected } from './utils.js'; +import logger from '../logger.js'; puppeteer.use(StealthPlugin()); @@ -33,13 +34,13 @@ export default async function execute(url, waitForSelector, options) { const statusCode = response.status(); if (botDetected(pageSource, statusCode)) { - console.warn('We have been detected as a bot :-/ Tried url: => ', url); + logger.warn('We have been detected as a bot :-/ Tried url: => ', url); return null; } return await page.content(); } catch (error) { - console.error('Error executing with puppeteer executor', error); + logger.error('Error executing with puppeteer executor', error); return null; } finally { if (browser != null) { diff --git a/lib/services/extractor/utils.js b/lib/services/extractor/utils.js index a356ece..af42f8d 100644 --- a/lib/services/extractor/utils.js +++ b/lib/services/extractor/utils.js @@ -1,3 +1,5 @@ +import logger from '../logger.js'; + let debuggingOn = false; export const DEFAULT_HEADER = { @@ -15,9 +17,7 @@ export const setDebug = (options) => { export const debug = (message) => { if (debuggingOn) { - /* eslint-disable no-console */ - console.debug(message); - /* eslint-enable no-console */ + logger.debug(message); } }; diff --git a/lib/services/logger.js b/lib/services/logger.js new file mode 100644 index 0000000..484bc8e --- /dev/null +++ b/lib/services/logger.js @@ -0,0 +1,15 @@ +import winston from 'winston'; + +const env = process.env.NODE_ENV || 'development'; + +const logger = winston.createLogger({ + level: env !== 'development' ? 'info' : 'debug', + format: winston.format.combine( + winston.format.colorize(), + winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), + winston.format.printf(({ timestamp, level, message }) => `[${timestamp}] ${level}: ${message}`), + ), + transports: [new winston.transports.Console()], +}); + +export default logger; diff --git a/lib/services/storage/jobStorage.js b/lib/services/storage/jobStorage.js index 6697b5c..eecb597 100644 --- a/lib/services/storage/jobStorage.js +++ b/lib/services/storage/jobStorage.js @@ -4,6 +4,7 @@ import * as listingStorage from './listingsStorage.js'; import { getDirName } from '../../utils.js'; import path from 'path'; import LowdashAdapter from './LowDashAdapter.js'; +import logger from '../logger.js'; const file = path.join(getDirName(), '../', 'db/jobs.json'); const adapter = new JSONFileSync(file); @@ -91,9 +92,7 @@ export const removeJobsByUserName = (userId) => { .value(); db.write(); if (removedDemoJobs > 0) { - /* eslint-disable no-console */ - console.log(`Removed ${removedDemoJobs} demo jobs`); - /* eslint-enable no-console */ + logger.info(`Removed ${removedDemoJobs} demo jobs`); } }; export const getJobs = () => { diff --git a/lib/services/tracking/Tracker.js b/lib/services/tracking/Tracker.js index 0e76b18..154f0d5 100644 --- a/lib/services/tracking/Tracker.js +++ b/lib/services/tracking/Tracker.js @@ -5,6 +5,7 @@ import os from 'os'; import { readFileSync } from 'fs'; import { packageUp } from 'package-up'; import fetch from 'node-fetch'; +import logger from '../logger.js'; const deviceId = getUniqueId() || 'N/A'; const version = await getPackageVersion(); @@ -37,7 +38,7 @@ export const trackMainEvent = async () => { } } } catch (error) { - console.warn('Error sending tracking data', error); + logger.warn('Error sending tracking data', error); } }; @@ -52,7 +53,7 @@ export async function trackDemoAccessed() { headers: { 'Content-Type': 'application/json' }, }); } catch (error) { - console.warn('Error sending tracking data', error); + logger.warn('Error sending tracking data', error); } } } @@ -84,7 +85,7 @@ async function getPackageVersion() { const json = JSON.parse(packageJson); return json.version; } catch (error) { - console.error('Error reading version from package.json', error); + logger.error('Error reading version from package.json', error); } return 'N/A'; } diff --git a/lib/utils.js b/lib/utils.js index 3f180c7..8c0ecd8 100755 --- a/lib/utils.js +++ b/lib/utils.js @@ -4,6 +4,7 @@ import { readFile } from 'fs/promises'; import { createHash } from 'crypto'; import { DEFAULT_CONFIG } from './defaultConfig.js'; import fs from 'fs'; +import logger from './services/logger.js'; const RE_GT = />/g; const RE_WEBP = /\/format\/webp/gi; @@ -74,8 +75,7 @@ export async function refreshConfig() { config.demoMode ??= false; } catch (error) { config = { ...DEFAULT_CONFIG }; - /* eslint-disable no-console */ - console.info('Error reading config file.', error); + logger.info('Error reading config file.', error); } } @@ -84,7 +84,7 @@ export async function refreshConfig() { */ const checkIfConfigExistsAndWriteIfNot = () => { if (!fs.existsSync(`${getDirName()}/../conf/config.json`)) { - console.info('Could not find config file. Will create one with default values now'); + logger.info('Could not find config file. Will create one with default values now'); fs.writeFileSync(`${getDirName()}/../conf/config.json`, JSON.stringify({ ...DEFAULT_CONFIG })); } }; diff --git a/package.json b/package.json index 84916cc..902abd2 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fredy", - "version": "11.6.3", + "version": "11.6.4", "description": "[F]ind [R]eal [E]states [d]amn eas[y].", "scripts": { "prepare": "husky", @@ -91,6 +91,7 @@ "serve-static": "2.2.0", "slack": "11.0.2", "vite": "7.1.5", + "winston": "^3.17.0", "x-var": "^2.1.0" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index d948e4e..b039bca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -938,6 +938,20 @@ "@babel/helper-string-parser" "^7.27.1" "@babel/helper-validator-identifier" "^7.27.1" +"@colors/colors@1.6.0", "@colors/colors@^1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.6.0.tgz#ec6cd237440700bc23ca23087f513c75508958b0" + integrity sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA== + +"@dabh/diagnostics@^2.0.2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.3.tgz#7f7e97ee9a725dffc7808d93668cc984e1dc477a" + integrity sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA== + dependencies: + colorspace "1.1.x" + enabled "2.0.x" + kuler "^2.0.0" + "@dnd-kit/accessibility@^3.1.1": version "3.1.1" resolved "https://registry.yarnpkg.com/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz#3b4202bd6bb370a0730f6734867785919beac6af" @@ -1754,6 +1768,11 @@ dependencies: undici-types "~7.10.0" +"@types/triple-beam@^1.3.2": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/triple-beam/-/triple-beam-1.3.5.tgz#74fef9ffbaa198eb8b588be029f38b00299caa2c" + integrity sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw== + "@types/unist@*", "@types/unist@^3.0.0": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.3.tgz#acaab0f919ce69cce629c2d4ed2eb4adc1b6c20c" @@ -2146,6 +2165,11 @@ async-validator@^3.5.0: resolved "https://registry.yarnpkg.com/async-validator/-/async-validator-3.5.2.tgz#68e866a96824e8b2694ff7a831c1a25c44d5e500" integrity sha512-8eLCg00W9pIRZSB781UUX/H6Oskmm8xloZfr09lz5bikRpBVDlJ3hRVuxxP1SxcwsEYfJ4IU8Q19Y8/893r3rQ== +async@^3.2.3: + version "3.2.6" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce" + integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -2573,16 +2597,52 @@ color-convert@2.0.1, color-convert@^2.0.1: dependencies: color-name "~1.1.4" -color-name@~1.1.4: +color-convert@^1.9.3: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +color-name@^1.0.0, color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +color-string@^1.6.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" + integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + +color@^3.1.3: + version "3.2.1" + resolved "https://registry.yarnpkg.com/color/-/color-3.2.1.tgz#3544dc198caf4490c3ecc9a790b54fe9ff45e164" + integrity sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA== + dependencies: + color-convert "^1.9.3" + color-string "^1.6.0" + colorette@^2.0.20: version "2.0.20" resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== +colorspace@1.1.x: + version "1.1.4" + resolved "https://registry.yarnpkg.com/colorspace/-/colorspace-1.1.4.tgz#8d442d1186152f60453bf8070cd66eb364e59243" + integrity sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w== + dependencies: + color "^3.1.3" + text-hex "1.0.x" + combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" @@ -3009,6 +3069,11 @@ emoji-regex@^9.2.2: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== +enabled@2.0.x: + version "2.0.0" + resolved "https://registry.yarnpkg.com/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2" + integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ== + encodeurl@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" @@ -3547,6 +3612,11 @@ fdir@^6.5.0: resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350" integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== +fecha@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.3.tgz#4d9ccdbc61e8629b259fdca67e65891448d569fd" + integrity sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw== + fetch-blob@^3.1.2, fetch-blob@^3.1.4: version "3.2.0" resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9" @@ -3617,6 +3687,11 @@ flatted@^3.2.9: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.3.tgz#67c8fad95454a7c7abebf74bb78ee74a44023358" integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg== +fn.name@1.x.x: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" + integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== + follow-redirects@^1.15.6: version "1.15.11" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.11.tgz#777d73d72a92f8ec4d2e410eb47352a56b8e8340" @@ -4178,6 +4253,11 @@ is-arrayish@^0.2.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== +is-arrayish@^0.3.1: + version "0.3.4" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.4.tgz#1ee5553818511915685d33bb13d31bf854e5059d" + integrity sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA== + is-async-function@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/is-async-function/-/is-async-function-2.1.1.tgz#3e69018c8e04e73b738793d020bfe884b9fd3523" @@ -4368,6 +4448,11 @@ is-shared-array-buffer@^1.0.4: dependencies: call-bound "^1.0.3" +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + is-string@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.1.1.tgz#92ea3f3d5c5b6e039ca8677e5ac8d07ea773cbb9" @@ -4574,6 +4659,11 @@ kind-of@^3.0.2: dependencies: is-buffer "^1.1.5" +kuler@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" + integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A== + lazy-cache@^0.2.3: version "0.2.7" resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-0.2.7.tgz#7feddf2dcb6edb77d11ef1d117ab5ffdf0ab1b65" @@ -4688,6 +4778,18 @@ log-update@^6.1.0: strip-ansi "^7.1.0" wrap-ansi "^9.0.0" +logform@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/logform/-/logform-2.7.0.tgz#cfca97528ef290f2e125a08396805002b2d060d1" + integrity sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ== + dependencies: + "@colors/colors" "1.6.0" + "@types/triple-beam" "^1.3.2" + fecha "^4.2.0" + ms "^2.1.1" + safe-stable-stringify "^2.3.1" + triple-beam "^1.3.0" + longest-streak@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-3.1.0.tgz#62fa67cd958742a1574af9f39866364102d90cd4" @@ -5665,6 +5767,13 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" +one-time@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/one-time/-/one-time-1.0.0.tgz#e06bc174aed214ed58edede573b433bbf827cb45" + integrity sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g== + dependencies: + fn.name "1.x.x" + onetime@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/onetime/-/onetime-7.0.0.tgz#9f16c92d8c9ef5120e3acd9dd9957cceecc1ab60" @@ -6228,7 +6337,7 @@ react@18.3.1: dependencies: loose-envify "^1.1.0" -readable-stream@^3.0.2, readable-stream@^3.1.1, readable-stream@^3.4.0: +readable-stream@^3.0.2, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.2: version "3.6.2" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== @@ -6587,6 +6696,11 @@ safe-regex-test@^1.1.0: es-errors "^1.3.0" is-regex "^1.2.1" +safe-stable-stringify@^2.3.1: + version "2.5.0" + resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz#4ca2f8e385f2831c432a719b108a3bf7af42a1dd" + integrity sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA== + "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -6808,6 +6922,13 @@ simple-statistics@^7.7.3: resolved "https://registry.yarnpkg.com/simple-statistics/-/simple-statistics-7.8.8.tgz#8acf4f78ba5a111e7c8424ffda2ac32fe353198c" integrity sha512-CUtP0+uZbcbsFpqEyvNDYjJCl+612fNgjT8GaVuvMG7tBuJg8gXGpsP5M7X658zy0IcepWOZ6nPBu1Qb9ezA1w== +simple-swizzle@^0.2.2: + version "0.2.4" + resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.4.tgz#a8d11a45a11600d6a1ecdff6363329e3648c3667" + integrity sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw== + dependencies: + is-arrayish "^0.3.1" + simple-update-notifier@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz#d70b92bdab7d6d90dfd73931195a30b6e3d7cebb" @@ -6904,6 +7025,11 @@ split-on-first@^3.0.0: resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-3.0.0.tgz#f04959c9ea8101b9b0bbf35a61b9ebea784a23e7" integrity sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA== +stack-trace@0.0.x: + version "0.0.10" + resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" + integrity sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg== + statuses@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" @@ -7186,6 +7312,11 @@ text-encoding@^0.6.4: resolved "https://registry.yarnpkg.com/text-encoding/-/text-encoding-0.6.4.tgz#e399a982257a276dae428bb92845cb71bdc26d19" integrity sha512-hJnc6Qg3dWoOMkqP53F0dzRIgtmsAge09kxUIqGrEUS4qr5rWLckGYaQAVr+opBrIMRErGgy6f5aPnyPpyGRfg== +text-hex@1.0.x: + version "1.0.0" + resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" + integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg== + tiny-json-http@^7.0.2: version "7.5.1" resolved "https://registry.yarnpkg.com/tiny-json-http/-/tiny-json-http-7.5.1.tgz#82efaa190c3edf6f5f2d906a9e88f792d38f8532" @@ -7235,6 +7366,11 @@ trim-lines@^3.0.0: resolved "https://registry.yarnpkg.com/trim-lines/-/trim-lines-3.0.1.tgz#d802e332a07df861c48802c04321017b1bd87338" integrity sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg== +triple-beam@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.4.1.tgz#6fde70271dc6e5d73ca0c3b24e2d92afb7441984" + integrity sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg== + trough@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/trough/-/trough-2.2.0.tgz#94a60bd6bd375c152c1df911a4b11d5b0256f50f" @@ -7603,6 +7739,32 @@ which@^2.0.1: dependencies: isexe "^2.0.0" +winston-transport@^4.9.0: + version "4.9.0" + resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.9.0.tgz#3bba345de10297654ea6f33519424560003b3bf9" + integrity sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A== + dependencies: + logform "^2.7.0" + readable-stream "^3.6.2" + triple-beam "^1.3.0" + +winston@^3.17.0: + version "3.17.0" + resolved "https://registry.yarnpkg.com/winston/-/winston-3.17.0.tgz#74b8665ce9b4ea7b29d0922cfccf852a08a11423" + integrity sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw== + dependencies: + "@colors/colors" "^1.6.0" + "@dabh/diagnostics" "^2.0.2" + async "^3.2.3" + is-stream "^2.0.0" + logform "^2.7.0" + one-time "^1.0.0" + readable-stream "^3.4.0" + safe-stable-stringify "^2.3.1" + stack-trace "0.0.x" + triple-beam "^1.3.0" + winston-transport "^4.9.0" + word-wrap@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34"