server: remove direct chalk dependency
Some checks failed
ci/woodpecker/push/lint-backend Pipeline failed
ci/woodpecker/push/build Pipeline was successful
ci/woodpecker/push/lint-sw Pipeline failed
ci/woodpecker/push/lint-foundkey-js Pipeline was successful
ci/woodpecker/push/lint-client Pipeline failed
ci/woodpecker/push/test Pipeline failed

Colouring of logs is not important to me because if I view them in journalctl
there are no colours anyway.
This commit is contained in:
Johann150 2024-04-01 19:10:55 +02:00
parent c9759a3a79
commit f245b6e517
Signed by: Johann150
GPG key ID: 9EE6577A2A06F8F1
19 changed files with 66 additions and 124 deletions

View file

@ -36,8 +36,6 @@
"bull": "4.8.4", "bull": "4.8.4",
"cacheable-lookup": "6.0.4", "cacheable-lookup": "6.0.4",
"cbor": "8.1.0", "cbor": "8.1.0",
"chalk": "5.0.1",
"chalk-template": "0.4.0",
"cli-highlight": "2.1.11", "cli-highlight": "2.1.11",
"color-convert": "2.0.1", "color-convert": "2.0.1",
"content-disposition": "0.5.4", "content-disposition": "0.5.4",

View file

@ -9,8 +9,8 @@ import 'reflect-metadata';
import { masterMain } from './master.js'; import { masterMain } from './master.js';
import { workerMain } from './worker.js'; import { workerMain } from './worker.js';
const logger = new Logger('core', 'cyan'); const logger = new Logger('core');
const clusterLogger = logger.createSubLogger('cluster', 'orange', false); const clusterLogger = logger.createSubLogger('cluster', false);
const ev = new Xev(); const ev = new Xev();
/** /**

View file

@ -3,8 +3,6 @@ import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path'; import { dirname } from 'node:path';
import * as os from 'node:os'; import * as os from 'node:os';
import cluster from 'node:cluster'; import cluster from 'node:cluster';
import chalk from 'chalk';
import chalkTemplate from 'chalk-template';
import semver from 'semver'; import semver from 'semver';
import Logger from '@/services/logger.js'; import Logger from '@/services/logger.js';
@ -19,29 +17,27 @@ const _dirname = dirname(_filename);
const meta = JSON.parse(fs.readFileSync(`${_dirname}/../../../../built/meta.json`, 'utf-8')); const meta = JSON.parse(fs.readFileSync(`${_dirname}/../../../../built/meta.json`, 'utf-8'));
const logger = new Logger('core', 'cyan'); const logger = new Logger('core');
const bootLogger = logger.createSubLogger('boot', 'magenta', false); const bootLogger = logger.createSubLogger('boot', false);
const themeColor = chalk.hex('#86b300');
function greet(): void { function greet(): void {
if (envOption.logLevel !== LOG_LEVELS.quiet) { if (envOption.logLevel !== LOG_LEVELS.quiet) {
//#region FoundKey logo //#region FoundKey logo
console.log(themeColor(' ___ _ _ __ ')); console.log(' ___ _ _ __ ');
console.log(themeColor(' | __|__ _ _ _ _ __| | |/ /___ _ _ ')); console.log(' | __|__ _ _ _ _ __| | |/ /___ _ _ ');
console.log(themeColor(' | _/ _ \\ || | \' \\/ _` | \' </ -_) || |')); console.log(' | _/ _ \\ || | \' \\/ _` | \' </ -_) || |');
console.log(themeColor(' |_|\\___/\\_,_|_||_\\__,_|_|\\_\\___|\\_, |')); console.log(' |_|\\___/\\_,_|_||_\\__,_|_|\\_\\___|\\_, |');
console.log(themeColor(' |__/ ')); console.log(' |__/ ');
//#endregion //#endregion
console.log(' FoundKey is an open-source decentralized microblogging platform.'); console.log(' FoundKey is an open-source decentralized microblogging platform.');
console.log(''); console.log('');
console.log(chalkTemplate`--- ${os.hostname()} {gray (PID: ${process.pid.toString()})} ---`); console.log(`--- ${os.hostname()} (PID: ${process.pid.toString()}) ---`);
} }
bootLogger.info('Welcome to FoundKey!'); bootLogger.info('Welcome to FoundKey!');
bootLogger.info(`FoundKey v${meta.version}`, true); bootLogger.info(`FoundKey v${meta.version}`);
} }
/** /**
@ -59,7 +55,7 @@ export async function masterMain(): Promise<void> {
config = loadConfigBoot(); config = loadConfigBoot();
await connectDb(); await connectDb();
} catch (e) { } catch (e) {
bootLogger.error('Fatal error occurred during initialization', true); bootLogger.error('Fatal error occurred during initialization');
process.exit(1); process.exit(1);
} }
@ -69,7 +65,7 @@ export async function masterMain(): Promise<void> {
await spawnWorkers(config.clusterLimits); await spawnWorkers(config.clusterLimits);
} }
bootLogger.succ(`Now listening on port ${config.port} on ${config.url}`, true); bootLogger.succ(`Now listening on port ${config.port} on ${config.url}`);
if (!envOption.noDaemons) { if (!envOption.noDaemons) {
import('../daemons/server-stats.js').then(x => x.serverStats()); import('../daemons/server-stats.js').then(x => x.serverStats());
@ -84,7 +80,7 @@ function showEnvironment(): void {
if (env !== 'production') { if (env !== 'production') {
logger.warn('The environment is not in production mode.'); logger.warn('The environment is not in production mode.');
logger.warn('DO NOT USE FOR PRODUCTION PURPOSE!', true); logger.warn('DO NOT USE FOR PRODUCTION PURPOSE!');
} }
} }
@ -109,7 +105,7 @@ function loadConfigBoot(): Config {
} catch (exception) { } catch (exception) {
const e = exception as Partial<NodeJS.ErrnoException> | Error; const e = exception as Partial<NodeJS.ErrnoException> | Error;
if ('code' in e && e.code === 'ENOENT') { if ('code' in e && e.code === 'ENOENT') {
configLogger.error('Configuration file not found', true); configLogger.error('Configuration file not found');
process.exit(1); process.exit(1);
} else if (e instanceof Error) { } else if (e instanceof Error) {
configLogger.error(e.message); configLogger.error(e.message);
@ -133,7 +129,7 @@ async function connectDb(): Promise<void> {
const v = await db.query('SHOW server_version').then(x => x[0].server_version); const v = await db.query('SHOW server_version').then(x => x[0].server_version);
dbLogger.succ(`Connected: v${v}`); dbLogger.succ(`Connected: v${v}`);
} catch (e) { } catch (e) {
dbLogger.error('Cannot connect', true); dbLogger.error('Cannot connect');
dbLogger.error(e as Error | string); dbLogger.error(e as Error | string);
process.exit(1); process.exit(1);
} }

View file

@ -1,7 +1,7 @@
import Logger from '@/services/logger.js'; import Logger from '@/services/logger.js';
import config from './index.js'; import config from './index.js';
const logger = new Logger('config:redis', 'gray', false); const logger = new Logger('config:redis', false);
function getRedisFamily(family?: string | number): number { function getRedisFamily(family?: string | number): number {
const familyMap = { const familyMap = {

View file

@ -72,7 +72,7 @@ import { getRedisOptions } from '@/config/redis.js';
import { dbLogger } from './logger.js'; import { dbLogger } from './logger.js';
import { redisClient } from './redis.js'; import { redisClient } from './redis.js';
const sqlLogger = dbLogger.createSubLogger('sql', 'gray', false); const sqlLogger = dbLogger.createSubLogger('sql', false);
class MyCustomLogger implements Logger { class MyCustomLogger implements Logger {
private highlight(sql: string): string { private highlight(sql: string): string {

View file

@ -1,7 +1,6 @@
import * as fs from 'node:fs'; import * as fs from 'node:fs';
import * as stream from 'node:stream'; import * as stream from 'node:stream';
import * as util from 'node:util'; import * as util from 'node:util';
import chalk from 'chalk';
import got, * as Got from 'got'; import got, * as Got from 'got';
import { SECOND, MINUTE } from '@/const.js'; import { SECOND, MINUTE } from '@/const.js';
import config from '@/config/index.js'; import config from '@/config/index.js';
@ -13,7 +12,7 @@ const pipeline = util.promisify(stream.pipeline);
export async function downloadUrl(url: string, path: string): Promise<void> { export async function downloadUrl(url: string, path: string): Promise<void> {
const logger = new Logger('download'); const logger = new Logger('download');
logger.info(`Downloading ${chalk.cyan(url)} ...`); logger.info(`Downloading ${url} ...`);
const timeout = 30 * SECOND; const timeout = 30 * SECOND;
const operationTimeout = MINUTE; const operationTimeout = MINUTE;
@ -65,5 +64,5 @@ export async function downloadUrl(url: string, path: string): Promise<void> {
} }
} }
logger.succ(`Download finished: ${chalk.cyan(url)}`); logger.succ(`Download finished: ${url}`);
} }

View file

@ -1,3 +1,3 @@
import Logger from '@/services/logger.js'; import Logger from '@/services/logger.js';
export const queueLogger = new Logger('queue', 'orange'); export const queueLogger = new Logger('queue');

View file

@ -45,6 +45,6 @@ export default async function cleanRemoteFiles(job: Bull.Job<Record<string, unkn
job.progress(deletedCount / total); job.progress(deletedCount / total);
} }
logger.succ('All cahced remote files has been deleted.'); logger.succ('All cached remote files have been deleted.');
done(); done();
} }

View file

@ -1,3 +1,3 @@
import { remoteLogger } from '../logger.js'; import { remoteLogger } from '../logger.js';
export const apLogger = remoteLogger.createSubLogger('ap', 'magenta'); export const apLogger = remoteLogger.createSubLogger('ap');

View file

@ -1,3 +1,3 @@
import Logger from '@/services/logger.js'; import Logger from '@/services/logger.js';
export const remoteLogger = new Logger('remote', 'cyan'); export const remoteLogger = new Logger('remote');

View file

@ -1,5 +1,4 @@
import { URL } from 'node:url'; import { URL } from 'node:url';
import chalk from 'chalk';
import { IsNull } from 'typeorm'; import { IsNull } from 'typeorm';
import { DAY } from '@/const.js'; import { DAY } from '@/const.js';
import { isSelfHost, toPuny } from '@/misc/convert-host.js'; import { isSelfHost, toPuny } from '@/misc/convert-host.js';
@ -46,7 +45,7 @@ export async function resolveUser(username: string, idnHost: string | null, reso
if (user == null) { if (user == null) {
const self = await resolveSelf(acctLower); const self = await resolveSelf(acctLower);
logger.succ(`return new remote user: ${chalk.magenta(acctLower)}`); logger.succ(`return new remote user: ${acctLower}`);
return await createPerson(self, resolver); return await createPerson(self, resolver);
} }
@ -101,16 +100,16 @@ export async function resolveUser(username: string, idnHost: string | null, reso
* Gets the Webfinger href matching rel="self". * Gets the Webfinger href matching rel="self".
*/ */
async function resolveSelf(acctLower: string): string { async function resolveSelf(acctLower: string): string {
logger.info(`WebFinger for ${chalk.yellow(acctLower)}`); logger.info(`WebFinger for ${acctLower}`);
// get webfinger response for user // get webfinger response for user
const finger = await webFinger(acctLower).catch(e => { const finger = await webFinger(acctLower).catch(e => {
logger.error(`Failed to WebFinger for ${chalk.yellow(acctLower)}: ${ e.statusCode || e.message }`); logger.error(`Failed to WebFinger for ${acctLower}: ${ e.statusCode || e.message }`);
throw new Error(`Failed to WebFinger for ${acctLower}: ${ e.statusCode || e.message }`); throw new Error(`Failed to WebFinger for ${acctLower}: ${ e.statusCode || e.message }`);
}); });
// try to find the rel="self" link // try to find the rel="self" link
const self = finger.links.find(link => link.rel?.toLowerCase() === 'self'); const self = finger.links.find(link => link.rel?.toLowerCase() === 'self');
if (!self?.href) { if (!self?.href) {
logger.error(`Failed to WebFinger for ${chalk.yellow(acctLower)}: self link not found`); logger.error(`Failed to WebFinger for ${acctLower}: self link not found`);
throw new Error('self link not found'); throw new Error('self link not found');
} }
return self.href; return self.href;

View file

@ -29,7 +29,7 @@ import proxyServer from './proxy/index.js';
import webServer from './web/index.js'; import webServer from './web/index.js';
import { initializeStreamingServer } from './api/streaming.js'; import { initializeStreamingServer } from './api/streaming.js';
export const serverLogger = new Logger('server', 'gray', false); export const serverLogger = new Logger('server', false);
// Init app // Init app
const app = new Koa(); const app = new Koa();

View file

@ -15,7 +15,7 @@ export default async function(blocker: User, blockee: User) {
}); });
if (blocking == null) { if (blocking == null) {
logger.warn('ブロック解除がリクエストされましたがブロックしていませんでした'); logger.warn('Unblock requested but not blocked');
return; return;
} }

View file

@ -12,7 +12,7 @@ import { getChartInsertLock } from '@/misc/app-lock.js';
import { db } from '@/db/postgre.js'; import { db } from '@/db/postgre.js';
import Logger from '../logger.js'; import Logger from '../logger.js';
const logger = new Logger('chart', 'white', process.env.NODE_ENV !== 'test'); const logger = new Logger('chart', process.env.NODE_ENV !== 'test');
const columnPrefix = '___' as const; const columnPrefix = '___' as const;
const uniqueTempColumnPrefix = 'unique_temp___' as const; const uniqueTempColumnPrefix = 'unique_temp___' as const;

View file

@ -25,7 +25,7 @@ import { IImage, convertSharpToJpeg, convertSharpToWebp, convertSharpToPng } fro
import { InternalStorage } from './internal-storage.js'; import { InternalStorage } from './internal-storage.js';
import { getS3 } from './s3.js'; import { getS3 } from './s3.js';
const logger = driveLogger.createSubLogger('register', 'yellow'); const logger = driveLogger.createSubLogger('register');
/*** /***
* Save file * Save file
@ -230,7 +230,7 @@ export async function generateAlts(path: string, type: string, generateWeb: bool
logger.debug('web image not created (not an required image)'); logger.debug('web image not created (not an required image)');
} }
} catch (err) { } catch (err) {
logger.warn('web image not created (an error occured)', err as Error); logger.warn('web image not created (an error occured)');
} }
} else { } else {
if (satisfyWebpublic) logger.info('web image not created (original satisfies webpublic)'); if (satisfyWebpublic) logger.info('web image not created (original satisfies webpublic)');

View file

@ -1,3 +1,3 @@
import Logger from '../logger.js'; import Logger from '../logger.js';
export const driveLogger = new Logger('drive', 'blue'); export const driveLogger = new Logger('drive');

View file

@ -9,7 +9,7 @@ import { Instances } from '@/models/index.js';
import { getFetchInstanceMetadataLock } from '@/misc/app-lock.js'; import { getFetchInstanceMetadataLock } from '@/misc/app-lock.js';
import Logger from './logger.js'; import Logger from './logger.js';
const logger = new Logger('metadata', 'cyan'); const logger = new Logger('metadata');
export async function fetchInstanceMetadata(instance: Instance, force = false): Promise<void> { export async function fetchInstanceMetadata(instance: Instance, force = false): Promise<void> {
const unlock = await getFetchInstanceMetadataLock(instance.host); const unlock = await getFetchInstanceMetadataLock(instance.host);

View file

@ -1,15 +1,7 @@
import cluster from 'node:cluster'; import cluster from 'node:cluster';
import chalk from 'chalk';
import convertColor from 'color-convert';
import { format as dateFormat } from 'date-fns'; import { format as dateFormat } from 'date-fns';
import config from '@/config/index.js'; import config from '@/config/index.js';
import { envOption, LOG_LEVELS } from '@/env.js'; import { envOption, LOG_LEVELS } from '@/env.js';
import type { KEYWORD } from 'color-convert/conversions.js';
type Domain = {
name: string;
color?: KEYWORD;
};
export type Level = LOG_LEVELS[keyof LOG_LEVELS]; export type Level = LOG_LEVELS[keyof LOG_LEVELS];
@ -17,7 +9,7 @@ export type Level = LOG_LEVELS[keyof LOG_LEVELS];
* Class that facilitates recording log messages to the console. * Class that facilitates recording log messages to the console.
*/ */
export default class Logger { export default class Logger {
private domain: Domain; private domain: string;
private parentLogger: Logger | null = null; private parentLogger: Logger | null = null;
private store: boolean; private store: boolean;
/** /**
@ -28,14 +20,10 @@ export default class Logger {
/** /**
* Create a logger instance. * Create a logger instance.
* @param domain Logging domain * @param domain Logging domain
* @param color Log message color
* @param store Whether to store messages * @param store Whether to store messages
*/ */
constructor(domain: string, color?: KEYWORD, store = true, minLevel?: Level) { constructor(domain: string, store = true, minLevel?: Level) {
this.domain = { this.domain = domain;
name: domain,
color,
};
this.store = store; this.store = store;
this.minLevel = minLevel ?? envOption.logLevel; this.minLevel = minLevel ?? envOption.logLevel;
} }
@ -43,12 +31,11 @@ export default class Logger {
/** /**
* Create a child logger instance. * Create a child logger instance.
* @param domain Logging domain * @param domain Logging domain
* @param color Log message color
* @param store Whether to store messages * @param store Whether to store messages
* @returns A Logger instance whose parent logger is this instance. * @returns A Logger instance whose parent logger is this instance.
*/ */
public createSubLogger(domain: string, color?: KEYWORD, store = true, minLevel?: Level): Logger { public createSubLogger(domain: string, store = true, minLevel?: Level): Logger {
const logger = new Logger(domain, color, store, minLevel); const logger = new Logger(domain, store, minLevel);
logger.parentLogger = this; logger.parentLogger = this;
return logger; return logger;
} }
@ -58,10 +45,9 @@ export default class Logger {
* @param level Indicates the level of this particular message. If it is * @param level Indicates the level of this particular message. If it is
* less than the minimum level configured, the message will be discarded. * less than the minimum level configured, the message will be discarded.
* @param message The message to be logged. * @param message The message to be logged.
* @param important Whether to highlight this message as especially important.
* @param subDomains Names of sub-loggers to be added. * @param subDomains Names of sub-loggers to be added.
*/ */
private log(level: Level, message: string, important = false, subDomains: Domain[] = [], _store = true): void { private log(level: Level, message: string, subDomains: Domain[] = [], _store = true): void {
const store = _store && this.store; const store = _store && this.store;
// Check against the configured log level. // Check against the configured log level.
@ -70,66 +56,52 @@ export default class Logger {
// If this logger has a parent logger, delegate the actual logging to it, // If this logger has a parent logger, delegate the actual logging to it,
// so the parent domain(s) will be logged properly. // so the parent domain(s) will be logged properly.
if (this.parentLogger) { if (this.parentLogger) {
this.parentLogger.log(level, message, important, [this.domain].concat(subDomains), store); this.parentLogger.log(level, message, [this.domain].concat(subDomains), store);
return; return;
} }
const time = dateFormat(new Date(), 'HH:mm:ss'); const time = dateFormat(new Date(), 'HH:mm:ss');
const worker = cluster.isPrimary ? '*' : cluster.worker?.id; const worker = cluster.isPrimary ? '*' : cluster.worker?.id;
const domains = [this.domain].concat(subDomains).map(d => d.color ? chalk.rgb(...convertColor.keyword.rgb(d.color))(d.name) : chalk.white(d.name)); const domains = [this.domain].concat(subDomains);
let levelDisplay; let levelDisplay;
let messageDisplay;
switch (level) { switch (level) {
case LOG_LEVELS.error: case LOG_LEVELS.error:
if (important) { levelDisplay = 'ERR ';
levelDisplay = chalk.bgRed.white('ERR ');
} else {
levelDisplay = chalk.red('ERR ');
}
messageDisplay = chalk.red(message);
break; break;
case LOG_LEVELS.warning: case LOG_LEVELS.warning:
levelDisplay = chalk.yellow('WARN'); levelDisplay = 'WARN';
messageDisplay = chalk.yellow(message);
break; break;
case LOG_LEVELS.success: case LOG_LEVELS.success:
if (important) { levelDisplay = 'DONE';
levelDisplay = chalk.bgGreen.white('DONE');
} else {
levelDisplay = chalk.green('DONE');
}
messageDisplay = chalk.green(message);
break; break;
case LOG_LEVELS.info: case LOG_LEVELS.info:
levelDisplay = chalk.blue('INFO'); levelDisplay = 'INFO';
messageDisplay = message;
break; break;
case LOG_LEVELS.debug: default: case LOG_LEVELS.debug:
levelDisplay = chalk.gray('VERB'); default:
messageDisplay = chalk.gray(message); levelDisplay = 'VERB';
break; break;
} }
let log = `${levelDisplay} ${worker}\t[${domains.join(' ')}]\t${messageDisplay}`; let log = `${levelDisplay} ${worker}\t[${domains.join(' ')}]\t${message}`;
if (envOption.withLogTime) log = chalk.gray(time) + ' ' + log; if (envOption.withLogTime) log = time + ' ' + log;
console.log(important ? chalk.bold(log) : log); console.log(log);
} }
/** /**
* Log an error message. * Log an error message.
* Use in situations where execution cannot be continued. * Use in situations where execution cannot be continued.
* @param err Error or string containing an error message * @param err Error or string containing an error message
* @param important Whether this error is important
*/ */
public error(err: string | Error, important = false): void { public error(err: string | Error): void {
if (err instanceof Error) { if (err instanceof Error) {
this.log(LOG_LEVELS.error, err.toString(), important); this.log(LOG_LEVELS.error, err.toString());
} else if (typeof err === 'object') { } else if (typeof err === 'object') {
this.log(LOG_LEVELS.error, `${(err as any).message || (err as any).name || err}`, important); this.log(LOG_LEVELS.error, `${(err as any).message || (err as any).name || err}`);
} else { } else {
this.log(LOG_LEVELS.error, `${err}`, important); this.log(LOG_LEVELS.error, `${err}`);
} }
} }
@ -137,39 +109,35 @@ export default class Logger {
* Log a warning message. * Log a warning message.
* Use in situations where execution can continue but needs to be improved. * Use in situations where execution can continue but needs to be improved.
* @param message Warning message * @param message Warning message
* @param important Whether this warning is important
*/ */
public warn(message: string, important = false): void { public warn(message: string): void {
this.log(LOG_LEVELS.warning, message, important); this.log(LOG_LEVELS.warning, message);
} }
/** /**
* Log a success message. * Log a success message.
* Use in situations where something has been successfully done. * Use in situations where something has been successfully done.
* @param message Success message * @param message Success message
* @param important Whether this success message is important
*/ */
public succ(message: string, important = false): void { public succ(message: string): void {
this.log(LOG_LEVELS.success, message, important); this.log(LOG_LEVELS.success, message);
} }
/** /**
* Log a debug message. * Log a debug message.
* Use for debugging (information needed by developers but not required by users). * Use for debugging (information needed by developers but not required by users).
* @param message Debug message * @param message Debug message
* @param important Whether this debug message is important
*/ */
public debug(message: string, important = false): void { public debug(message: string): void {
this.log(LOG_LEVELS.debug, message, important); this.log(LOG_LEVELS.debug, message);
} }
/** /**
* Log an informational message. * Log an informational message.
* Use when something needs to be logged but doesn't fit into other levels. * Use when something needs to be logged but doesn't fit into other levels.
* @param message Info message * @param message Info message
* @param important Whether this info message is important
*/ */
public info(message: string, important = false): void { public info(message: string): void {
this.log(LOG_LEVELS.info, message, important); this.log(LOG_LEVELS.info, message);
} }
} }

View file

@ -3675,8 +3675,6 @@ __metadata:
bull: 4.8.4 bull: 4.8.4
cacheable-lookup: 6.0.4 cacheable-lookup: 6.0.4
cbor: 8.1.0 cbor: 8.1.0
chalk: 5.0.1
chalk-template: 0.4.0
cli-highlight: 2.1.11 cli-highlight: 2.1.11
color-convert: 2.0.1 color-convert: 2.0.1
content-disposition: 0.5.4 content-disposition: 0.5.4
@ -4344,15 +4342,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"chalk-template@npm:0.4.0":
version: 0.4.0
resolution: "chalk-template@npm:0.4.0"
dependencies:
chalk: ^4.1.2
checksum: 6c706802a79a7963cbce18f022b046fe86e438a67843151868852f80ea7346e975a6a9749991601e7e5d3b6a6c4852a04c53dc966a9a3d04031bd0e0ed53c819
languageName: node
linkType: hard
"chalk@npm:4.0.0": "chalk@npm:4.0.0":
version: 4.0.0 version: 4.0.0
resolution: "chalk@npm:4.0.0" resolution: "chalk@npm:4.0.0"
@ -4363,13 +4352,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"chalk@npm:5.0.1":
version: 5.0.1
resolution: "chalk@npm:5.0.1"
checksum: 7b45300372b908f0471fbf7389ce2f5de8d85bb949026fd51a1b95b10d0ed32c7ed5aab36dd5e9d2bf3191867909b4404cef75c5f4d2d1daeeacd301dd280b76
languageName: node
linkType: hard
"chalk@npm:^1.1.3": "chalk@npm:^1.1.3":
version: 1.1.3 version: 1.1.3
resolution: "chalk@npm:1.1.3" resolution: "chalk@npm:1.1.3"
@ -4394,7 +4376,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"chalk@npm:^4.0.0, chalk@npm:^4.0.2, chalk@npm:^4.1.0, chalk@npm:^4.1.2": "chalk@npm:^4.0.0, chalk@npm:^4.0.2, chalk@npm:^4.1.0":
version: 4.1.2 version: 4.1.2
resolution: "chalk@npm:4.1.2" resolution: "chalk@npm:4.1.2"
dependencies: dependencies: