diff --git a/.c8rc.json b/.c8rc.json index 4ebfce7..a01c3b3 100644 --- a/.c8rc.json +++ b/.c8rc.json @@ -1,8 +1,8 @@ { "exclude": ["**/test/**", "**/example/**"], "check-coverage": true, - "branches": 90, - "lines": 90, - "functions": 90, - "statements": 90 + "branches": 100, + "lines": 100, + "functions": 100, + "statements": 100 } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 0f83b60..781bf46 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,51 @@ { + // Disable the default formatter, use eslint instead + "prettier.enable": false, + "editor.formatOnSave": false, + + // Auto fix "editor.codeActionsOnSave": { - "source.fixAll": "explicit" + "source.fixAll.eslint": "explicit", + "source.organizeImports": "never" }, + + // Silent the stylistic rules in you IDE, but still auto fix them + "eslint.rules.customizations": [ + { "rule": "style/*", "severity": "off", "fixable": true }, + { "rule": "format/*", "severity": "off", "fixable": true }, + { "rule": "*-indent", "severity": "off", "fixable": true }, + { "rule": "*-spacing", "severity": "off", "fixable": true }, + { "rule": "*-spaces", "severity": "off", "fixable": true }, + { "rule": "*-order", "severity": "off", "fixable": true }, + { "rule": "*-dangle", "severity": "off", "fixable": true }, + { "rule": "*-newline", "severity": "off", "fixable": true }, + { "rule": "*quotes", "severity": "off", "fixable": true }, + { "rule": "*semi", "severity": "off", "fixable": true } + ], + + // Enable eslint for all supported languages + "eslint.validate": [ + "javascript", + "javascriptreact", + "typescript", + "typescriptreact", + "vue", + "html", + "markdown", + "json", + "jsonc", + "yaml", + "toml", + "xml", + "gql", + "graphql", + "astro", + "css", + "less", + "scss", + "pcss", + "postcss" + ], "mochaExplorer.files": "**/dist/test/**/*.test.js", - "typescript.tsdk": "node_modules/typescript/lib", + "typescript.tsdk": "node_modules/typescript/lib" } \ No newline at end of file diff --git a/package.json b/package.json index dfd5a4a..98515c5 100644 --- a/package.json +++ b/package.json @@ -4,23 +4,22 @@ "private": true, "type": "module", "scripts": { - "build": "tsc --build", - "docgen": "typedoc", - "lint": "eslint **/src/**/*.ts **/test/**/*.ts", - "lintfix": "eslint **/src/**/*.ts **/test/**/*.ts --fix", - "coverage": "c8 mocha **/test/**/*.test.js", - "test": "mocha **/test/**/*.test.js", - "bt": "pnpm run build && pnpm run test", - "bc": "pnpm run build && pnpm run coverage" + "build": "tsc --build", + "docgen": "typedoc", + "lint": "eslint **/src/**/*.ts **/test/**/*.ts", + "lintfix": "eslint **/src/**/*.ts **/test/**/*.ts --fix", + "coverage": "c8 mocha **/test/**/*.test.js", + "test": "mocha **/test/**/*.test.js", + "bt": "pnpm run build && pnpm run test", + "bc": "pnpm run build && pnpm run coverage" }, "devDependencies": { "@types/mocha": "^10.0.7", + "c8": "^10.1.2", "eslint": "^8.57.0", "eslint-config-love": "^59.0.0", "mocha": "^10.7.0", - "c8": "^10.1.2", - "typescript": "^5.5.4", - "typedoc": "^0.26.3" + "typedoc": "^0.26.5", + "typescript": "^5.5.4" } } - diff --git a/packages/logasjson/package.json b/packages/logasjson/package.json index 507b1a1..db198d2 100644 --- a/packages/logasjson/package.json +++ b/packages/logasjson/package.json @@ -1,14 +1,11 @@ { "name": "logasjson", - "version": "0.0.1", + "version": "1.0.0", "description": "JSON Logger with adapter for loki.", "main": "dist/src/index.js", "sideEffects": false, "exports": { - "./classes/*": "./dist/src/classes/*.js", - "./helper/*": "./dist/src/helper/*.js", - "./types/*": "./dist/src/types/*.js", - "./enums/*": "./dist/src/enums/*.js" + ".": "./dist/src/index.js" }, "files": [ "/dist/src", diff --git a/packages/logasjson/src/classes/Context.ts b/packages/logasjson/src/classes/Context.ts deleted file mode 100644 index 386aabc..0000000 --- a/packages/logasjson/src/classes/Context.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { AsyncLocalStorage } from 'async_hooks' -import { randomUUID } from 'crypto' - -import { Logger } from './Logger.js' -import type { ContextOptions, ContextResultSafe } from '../types/context.js' -import type { LoggerOptions } from '../types/logger.js' - -export class Context { - static #storage: AsyncLocalStorage - static #logger: Logger - - readonly #store: Map - - public readonly logger: LoggerOptions - public readonly name: string - public readonly traceId: string - - constructor (options: ContextOptions) { - if (Context.#storage === undefined) { - Context.#storage = new AsyncLocalStorage() - Context.#logger = new Logger('context') - } - - const session = (options.inherit ?? true) ? Context.getStore() : undefined - - this.name = Logger.mergeName(session?.name, options.name ?? '') - this.traceId = options.traceId ?? session?.traceId ?? randomUUID() - this.logger = Logger.mergeOptions(session?.logger, options.logger) - - if (options.store !== undefined) { - this.#store = options.store instanceof Map ? options.store : new Map(options.store) - } else { - this.#store = new Map() - } - } - - public get any)>(key: string, type: T): ContextResultSafe | undefined { - const d = this.getUnsafe(key) - if (d === undefined) return undefined - if (typeof type === 'string') { - // eslint-disable-next-line valid-typeof - if (typeof d !== type) throw new Error('typeof mismatch') - } else if (!(d instanceof (type as (abstract new (...args: any[]) => any)))) throw new Error('instanceof mismatch') - return d - } - - public getOrSet any)>(key: string, type: T, factory: () => ContextResultSafe): ContextResultSafe { - let d = this.get(key, type) - if (d === undefined) { - d = factory() - this.#store.set(key, d) - } - return d as any as ContextResultSafe - } - - public getUnsafe(key: string): T | undefined { - const d = this.#store.get(key) - if (d === undefined) return undefined - return d - } - - public getOrSetUnsafe(key: string, factory: () => T): T { - let d = this.#store.get(key) - if (d === undefined) { - d = factory() - this.#store.set(key, d) - } - return d - } - - public has (key: string): boolean { - return this.#store.has(key) - } - - public set (key: string, data: any): void { - this.#store.set(key, data) - } - - public delete (key: string): void { - this.#store.delete(key) - } - - public run(callback: (...args: TArgs) => R, ...args: TArgs): R { - return Context.#storage.run(this, callback, ...args) - } - - public runCatch(callback: (...args: TArgs) => R, ...args: TArgs): R | undefined { - try { - const res = this.run(callback, ...args) - if (res instanceof Promise) return res.catch(e => Context.#logger.error?.('runCatch - promise rejected', { error: e })) as any - return res - } catch (e) { - Context.#logger.error?.('runCatch - error thrown', { error: e }) - } - } - - public static run(options: ContextOptions, callback: (...args: TArgs) => R, ...args: TArgs): R { - const context = new Context(options) - return context.run(callback, ...args) - } - - public static runCatch(options: ContextOptions, callback: (...args: TArgs) => R, ...args: TArgs): R | undefined { - const context = new Context(options) - return context.runCatch(callback, ...args) - } - - public static getStore (): Context | undefined { - return this.#storage?.getStore() - } - - public static exit(callback: (...args: TArgs) => R, ...args: TArgs): R { - return this.#storage.exit(callback, ...args) - } - - public static exitCatch(callback: (...args: TArgs) => R, ...args: TArgs): R | undefined { - try { - const res = this.#storage.exit(callback, ...args) - if (res instanceof Promise) return res.catch(e => Context.#logger.error?.('exitCatch - promise rejected', { error: e })) as any - return res - } catch (e) { - Context.#logger.error?.('exitCatch - error thrown', { error: e }) - } - } - - public static enterWith (options: ContextOptions): void { - const context = new Context(options) - this.#storage.enterWith(context) - } - - public static restart (): void { - if (Context.#storage === undefined) return - Context.#storage.disable() - Context.#storage = new AsyncLocalStorage() - } -} diff --git a/packages/logasjson/src/classes/Logger.ts b/packages/logasjson/src/classes/Logger.ts index 99eafc1..1328745 100644 --- a/packages/logasjson/src/classes/Logger.ts +++ b/packages/logasjson/src/classes/Logger.ts @@ -1,129 +1,21 @@ -/* eslint-disable @typescript-eslint/max-params */ -import { randomUUID } from 'crypto' - -import { Context } from './Context.js' import { Override } from './Override.js' -import { LogLevel } from '../enums/loglevel.js' -import { placeholder } from '../helper/placeholder.js' -import type { LoggerEntry, LoggerLog, LoggerOptions } from '../types/logger.js' - -export class Logger { - public static options: LoggerOptions = { - - } - - public static readonly override = new Override() - - public readonly name: string - private readonly options: LoggerOptions - - get logLevel (): LogLevel { - const context = Context.getStore() - return this.contextLogLevel(context) - } - - get traceId (): string { - const context = Context.getStore() - return context?.traceId ?? randomUUID() - } - - constructor (name: string = '', options: LoggerOptions = {}) { - this.name = name - this.options = options - } - - extend (name: string = '', options: LoggerOptions = {}): Logger { - return new Logger(Logger.mergeName(this.name, name), Logger.mergeOptions(this.options, options)) - } - - get debug (): LoggerLog | undefined { - return this.log(LogLevel.Debug) - } - - get info (): LoggerLog | undefined { - return this.log(LogLevel.Info) - } +import type { LoggerInitOptions, LoggerOptions } from '../types/logger.js' +import { LoggerFork } from './LoggerFork.js' +import { DestinationConsole } from './destination/DestinationConsole.js' - get warn (): LoggerLog | undefined { - return this.log(LogLevel.Warn) +export class Logger extends LoggerFork { + public get override (): Override { + return this.options.override } - get error (): LoggerLog | undefined { - return this.log(LogLevel.Error) + public constructor (options: LoggerInitOptions) { + if(options.override === undefined) options.override = new Override() + if(options.name === '') options.name = undefined + if(options.destination === undefined) options.destination = new DestinationConsole() + super(options as LoggerOptions) } public async flush (): Promise { - const context = Context.getStore() - const destination = context?.logger.destination ?? this.options.destination ?? Logger.options.destination - await destination?.flush?.() - } - - private contextLogLevel (context: Context | undefined): number { - const logLevel = Logger.override.getLogLevel(this.name, context?.name) - if (logLevel !== undefined) return logLevel - return context?.logger.logLevel ?? this.options.logLevel ?? Logger.options.logLevel ?? LogLevel.None - } - - private log (logLevel: LogLevel): LoggerLog | undefined { - const context = Context.getStore() - if (logLevel < this.contextLogLevel(context)) return undefined - return (message: string, data?: Record, trace?: boolean): void => { - const destination = context?.logger.destination ?? this.options.destination ?? Logger.options.destination - destination?.write(Logger.processDataRaw(context, logLevel, this.name, this.options, message, data, trace)) - } - } - - private static processDataRaw ( - context: Context | undefined, - logLevel: LogLevel, - name: string, - options: LoggerOptions, - message: string, - data?: Record, - trace?: boolean): LoggerEntry { - const d: LoggerEntry = Object.assign({}, Logger.options.data, options.data, data, context?.logger.data) - d.logger = name - - if (context !== undefined) { - d.traceId = context.traceId - d.context = context.name - } - - d.logLevel = logLevel - - d.message = placeholder(message, d) - if (trace === true) d.trace = (new Error()).stack - d.timestamp = Date.now() - - return d - } - - public static processData ( - logLevel: LogLevel, - name: string, - options: LoggerOptions, - message: string, - data?: Record, - trace?: boolean): LoggerEntry { - return Logger.processDataRaw(Context.getStore(), logLevel, name, options, message, data, trace) - } - - public static mergeOptions ( - opt1: LoggerOptions = {}, - opt2: LoggerOptions = {} - ): LoggerOptions { - return { - logLevel: opt2.logLevel ?? opt1.logLevel, - destination: opt2.destination ?? opt1.destination, - data: Object.assign({}, opt1.data, opt2.data) - } - } - - public static async flush (): Promise { - await Logger.options.destination?.flush?.() - } - - public static mergeName (name1: string | undefined, name2: string): string { - return [name1, name2].filter(e => e !== undefined).join(':') + await this.options.destination?.flush?.() } } diff --git a/packages/logasjson/src/classes/LoggerFork.ts b/packages/logasjson/src/classes/LoggerFork.ts new file mode 100644 index 0000000..ad80db8 --- /dev/null +++ b/packages/logasjson/src/classes/LoggerFork.ts @@ -0,0 +1,64 @@ +/* eslint-disable @typescript-eslint/max-params */ + +import { LogLevel } from '../enums/loglevel.js' +import type { LoggerOptions, LoggerLog, LoggerForkOptions, LoggerContext } from '../types/logger.js' +import { processLog } from '../helper/processLog.js' + +export class LoggerFork { + protected readonly options: LoggerOptions + + get logLevel (): LogLevel { + const context = this.options.context?.() + return this.#contextLogLevel(context) + } + + get traceId (): string | undefined { + const context = this.options.context?.() + return context?.traceId + } + + constructor (options: LoggerOptions) { + this.options = options + } + + fork (options: LoggerForkOptions = {}): LoggerFork { + return new LoggerFork({ + logLevel: options.logLevel ?? this.options.logLevel, + destination: this.options.destination, + override: this.options.override, + data: Object.assign({}, this.options.data, options.data), + context: this.options.context, + name: [this.options.name, options.name].filter(e => e !== undefined && e !== "").join(':') + }) + } + + get debug (): LoggerLog | undefined { + return this.#log(LogLevel.Debug) + } + + get info (): LoggerLog | undefined { + return this.#log(LogLevel.Info) + } + + get warn (): LoggerLog | undefined { + return this.#log(LogLevel.Warn) + } + + get error (): LoggerLog | undefined { + return this.#log(LogLevel.Error) + } + + #contextLogLevel (context: LoggerContext | undefined): number { + const logLevel = this.options.override.getLogLevel(this.options.name ?? '', context?.name) + if (logLevel !== undefined) return logLevel + return context?.logLevel ?? this.options.logLevel + } + + #log (logLevel: LogLevel): LoggerLog | undefined { + const context = this.options.context?.() + if (logLevel < this.#contextLogLevel(context)) return undefined + return (message: string, data?: Record, trace?: boolean): void => { + this.options.destination.write(processLog(context, logLevel, this.options.name ?? '', this.options, message, data, trace)) + } + } +} diff --git a/packages/logasjson/src/classes/Override.ts b/packages/logasjson/src/classes/Override.ts index fc28d09..0634f9b 100644 --- a/packages/logasjson/src/classes/Override.ts +++ b/packages/logasjson/src/classes/Override.ts @@ -7,40 +7,40 @@ export class Override { return this.#data } - private toKey (context: string | undefined, logger: string | undefined): string { + #toKey (context: string | undefined, logger: string | undefined): string { return JSON.stringify([context, logger]) } getLogLevel (logger: string, context: string | undefined): LogLevel | undefined { - let logLevel = this.#data.get(this.toKey(context, logger)) + let logLevel = this.#data.get(this.#toKey(context, logger)) if (logLevel !== undefined) return logLevel - if (context !== undefined) logLevel = this.#data.get(this.toKey(context, undefined)) + if (context !== undefined) logLevel = this.#data.get(this.#toKey(context, undefined)) if (logLevel !== undefined) return logLevel - return this.#data.get(this.toKey(undefined, logger)) + return this.#data.get(this.#toKey(undefined, logger)) } setByContext (logLevel: LogLevel, context: string): void { - this.#data.set(this.toKey(context, undefined), logLevel) + this.#data.set(this.#toKey(context, undefined), logLevel) } deleteByContext (context: string): void { - this.#data.delete(this.toKey(context, undefined)) + this.#data.delete(this.#toKey(context, undefined)) } setByLogger (logLevel: LogLevel, logger: string): void { - this.#data.set(this.toKey(undefined, logger), logLevel) + this.#data.set(this.#toKey(undefined, logger), logLevel) } deleteByLogger (logger: string): void { - this.#data.delete(this.toKey(undefined, logger)) + this.#data.delete(this.#toKey(undefined, logger)) } set (logLevel: LogLevel, logger?: string, context?: string): void { - this.#data.set(this.toKey(context, logger), logLevel) + this.#data.set(this.#toKey(context, logger), logLevel) } delete (logger?: string, context?: string): void { - this.#data.delete(this.toKey(context, logger)) + this.#data.delete(this.#toKey(context, logger)) } clear (): void { diff --git a/packages/logasjson/src/classes/destination/DestinationBatch.ts b/packages/logasjson/src/classes/destination/DestinationBatch.ts index add04a1..129b516 100644 --- a/packages/logasjson/src/classes/destination/DestinationBatch.ts +++ b/packages/logasjson/src/classes/destination/DestinationBatch.ts @@ -13,7 +13,7 @@ export class DestinationBatch implements LoggerDestination { this.#timeout = undefined } - private run (): void { + #run (): void { this.#timeout = undefined if (this.#data !== undefined) this.#destination.send(this.#data) this.#data = undefined @@ -22,12 +22,12 @@ export class DestinationBatch implements LoggerDestination { public write (data: LoggerEntry): void { this.#data = this.#destination.process(this.#data, data) if (this.#timeout !== undefined) return - this.#timeout = setTimeout(() => { this.run() }, this.#interval) as any + this.#timeout = setTimeout(() => { this.#run() }, this.#interval) as any } public async flush (): Promise { clearTimeout(this.#timeout) - this.run() + this.#run() await this.#destination.flush?.() } } diff --git a/packages/logasjson/src/classes/destination/DestinationLoki.ts b/packages/logasjson/src/classes/destination/DestinationLoki.ts index ae67dd2..5838c96 100644 --- a/packages/logasjson/src/classes/destination/DestinationLoki.ts +++ b/packages/logasjson/src/classes/destination/DestinationLoki.ts @@ -11,8 +11,7 @@ import { serializeError } from 'serialize-error' import { LogLevel } from '../../enums/loglevel.js' import type { LoggerDestination, LoggerDestinationBatch, LoggerEntry } from '../../types/logger.js' import type { LokiOptions, LokiStream } from '../../types/loki.js' -import { Logger } from '../Logger.js' - +import { processLog } from '../../helper/processLog.js' export class DestinationLoki implements LoggerDestinationBatch> { readonly #url: URL readonly #fallback: LoggerDestination | undefined @@ -35,7 +34,7 @@ export class DestinationLoki implements LoggerDestinationBatch() const key = JSON.stringify([data.context, data.logger, data.traceId, data.logLevel, ...Object.values(this.#labels)]) const d = p.get(key) - if (d === undefined) p.set(key, { stream: Object.assign({ logger: data.logger, context: data.context, level: this.grafanaLevel(data.logLevel) }, this.#labels), values: [[data.timestamp.toString() + '000000', jc.stringify(serializeError(data))]] }) + if (d === undefined) p.set(key, { stream: Object.assign({ logger: data.logger, context: data.context, level: this.#grafanaLevel(data.logLevel) }, this.#labels), values: [[data.timestamp.toString() + '000000', jc.stringify(serializeError(data))]] }) else d.values.push([data.timestamp.toString() + '000000', jc.stringify(serializeError(data)) as string]) return p } @@ -43,8 +42,8 @@ export class DestinationLoki implements LoggerDestinationBatch): void { const d = [...data.values()] this.#promise = this.#promise.then(async () => { - await this.post(d).catch((e: Error) => { - this.#fallback?.write(Logger.processData(LogLevel.Error, 'context:loki', {}, e.message, { failed: d }, false)) + await this.#post(d).catch((e: Error) => { + this.#fallback?.write(processLog(undefined, LogLevel.Error, 'context:loki', {}, e.message, { failed: d }, false)) }) }) } @@ -58,7 +57,7 @@ export class DestinationLoki implements LoggerDestinationBatch = {}, timeout: number = 1000): Promise { + async #post (data: LokiStream[], headers: Record = {}, timeout: number = 1000): Promise { const input = JSON.stringify({ streams: data }) const buffer = this.#compression diff --git a/packages/logasjson/src/helper/filter.ts b/packages/logasjson/src/helper/filter.ts deleted file mode 100644 index 377c133..0000000 --- a/packages/logasjson/src/helper/filter.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { LogLevel } from '../enums/loglevel.js' - -const v4 = /^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i - -export function filterTraceId (traceId: string | string[] | undefined): string | undefined { - if (traceId === undefined || Array.isArray(traceId) || !v4.test(traceId)) return undefined - return traceId -} - -export function filterLogLevel (logLevel: string | string[] | undefined): LogLevel | undefined { - if (logLevel === undefined || Array.isArray(logLevel)) return undefined - const loglevel = parseInt(logLevel) - if (isNaN(loglevel) || loglevel < 0 || loglevel > 4) return undefined - return loglevel -} diff --git a/packages/logasjson/src/helper/processLog.ts b/packages/logasjson/src/helper/processLog.ts new file mode 100644 index 0000000..9931d85 --- /dev/null +++ b/packages/logasjson/src/helper/processLog.ts @@ -0,0 +1,30 @@ +import type { LogLevel } from "../enums/loglevel.js" +import type { LoggerForkOptions, LoggerEntry, LoggerContext } from "../types/logger.js" +import { placeholder } from "./placeholder.js" + +// eslint-disable-next-line @typescript-eslint/max-params +export function processLog ( + context: LoggerContext | undefined, + logLevel: LogLevel, + name: string, + options: LoggerForkOptions, + message: string, + data?: Record, + trace?: boolean): LoggerEntry { + const d = Object.assign({}, options.data, context?.data, data) + + d.logger = name + + if (context !== undefined) { + d.traceId = context?.traceId + d.context = context?.name + } + + d.logLevel = logLevel + + d.message = placeholder(message, d) + if (trace === true) d.trace = (new Error()).stack + d.timestamp = Date.now() + + return d as LoggerEntry + } \ No newline at end of file diff --git a/packages/logasjson/src/index.ts b/packages/logasjson/src/index.ts new file mode 100644 index 0000000..1a3e49b --- /dev/null +++ b/packages/logasjson/src/index.ts @@ -0,0 +1,13 @@ +export { Logger } from './classes/Logger.js' +export { LoggerFork } from './classes/LoggerFork.js' +export { Override } from './classes/Override.js' +export { DestinationArray } from './classes/destination/DestinationArray.js' +export { DestinationBatch } from './classes/destination/DestinationBatch.js' +export { DestinationConsole } from './classes/destination/DestinationConsole.js' +export { DestinationJson } from './classes/destination/DestinationJson.js' +export { DestinationLoki } from './classes/destination/DestinationLoki.js' +export { DestinationMessage } from './classes/destination/DestinationMessage.js' + +export type * from './types/logger.js' +export type * from './types/loki.js' +export * from './enums/loglevel.js' \ No newline at end of file diff --git a/packages/logasjson/src/types/context.ts b/packages/logasjson/src/types/context.ts deleted file mode 100644 index 3e68f4a..0000000 --- a/packages/logasjson/src/types/context.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { LoggerOptions } from './logger.js' - -export interface ContextOptions { - name?: string - traceId?: string - logger?: LoggerOptions - store?: Map | Array<[string, any]> - inherit?: boolean -} - -export type ContextResultSafe = T extends abstract new (...args: any[]) => any ? InstanceType : (T extends 'number' ? number : T extends 'string' ? string : (T extends 'boolean' ? boolean : never)) diff --git a/packages/logasjson/src/types/logger.ts b/packages/logasjson/src/types/logger.ts index 5c3c490..dab18bf 100644 --- a/packages/logasjson/src/types/logger.ts +++ b/packages/logasjson/src/types/logger.ts @@ -1,24 +1,36 @@ +import type { Override } from '../classes/Override.js' import type { LogLevel } from '../enums/loglevel.js' + +export interface LoggerContext extends LoggerForkOptions { + traceId?: string | undefined +} + export interface LoggerEntry { - context?: string - logger?: string + context?: string | undefined + logger?: string | undefined timestamp: number logLevel: LogLevel message: string - traceId?: string + traceId?: string | undefined [key: string]: any } +export type LoggerInitOptions = Omit & { override?: Override | undefined, destination?: LoggerDestination | undefined } + export interface LoggerOptions { - logLevel?: LogLevel | undefined + logLevel: LogLevel data?: Record | undefined - destination?: LoggerDestination | undefined + name?: string | undefined + destination: LoggerDestination + override: Override + context?: (() => LoggerContext | undefined) | undefined } -export interface LoggerStaticOverride { - logger: string | undefined - context: string | undefined +export interface LoggerForkOptions { + logLevel?: LogLevel | undefined + data?: Record | undefined + name?: string | undefined } export type LoggerLog = (message: string, data?: Record, trace?: boolean) => void diff --git a/packages/logasjson/test/batch.test.ts b/packages/logasjson/test/batch.test.ts new file mode 100644 index 0000000..714e506 --- /dev/null +++ b/packages/logasjson/test/batch.test.ts @@ -0,0 +1,80 @@ +/* eslint-disable @typescript-eslint/no-misused-promises */ +import assert from 'assert/strict' +import { setTimeout as sleep } from 'timers/promises' + +import { DestinationBatch } from '../src/classes/destination/DestinationBatch.js' +import { LogLevel } from '../src/enums/loglevel.js' +import { Logger } from '../src/classes/Logger.js' + +describe('batch', () => { + it('destination', async () => { + let process = 0 + let raw = 0 + const b = new DestinationBatch(0, { + process: (p, data) => { + if (p === undefined) p = [] + assert.equal(data.message, 'test') + p.push('test') + process++ + return p + }, + send: (data: string[]) => { + assert.equal(data.length, 2) + raw++ + } + }) + + const t = new Logger( { name: "test", destination: b, logLevel: LogLevel.Error }) + + t?.error?.('test') + assert.equal(process, 1) + t?.error?.('test') + assert.equal(process, 2) + + assert.equal(raw, 0) + + await sleep(0) + + assert.equal(process, 2) + assert.equal(raw, 1) + + await t.flush() + }) + + it('flush', async () => { + let process = 0 + let raw = 0 + const b = new DestinationBatch(0, { + process: (p, data) => { + if (p === undefined) p = [] + assert.equal(data.message, 'test') + p.push('test') + process++ + return p + }, + send: (data: string[]) => { + assert.equal(data.length, 2) + raw++ + }, + async flush(): Promise { + await sleep(10) + } + }) + + const t = new Logger({ destination: b, logLevel: LogLevel.Error, name: '' }) + + t?.error?.('test') + assert.equal(process, 1) + t?.error?.('test') + assert.equal(process, 2) + + assert.equal(raw, 0) + + await t.flush() + + assert.equal(process, 2) + assert.equal(raw, 1) + + }) +}) + diff --git a/packages/logasjson/test/batch.ts b/packages/logasjson/test/batch.ts deleted file mode 100644 index a2fde03..0000000 --- a/packages/logasjson/test/batch.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* eslint-disable @typescript-eslint/no-misused-promises */ -import assert from 'assert/strict' -import { setTimeout as sleep } from 'timers/promises' - -import { DestinationBatch } from '../src/classes/destination/DestinationBatch.js' -import { Logger } from '../src/classes/Logger.js' -import { LogLevel } from '../src/enums/loglevel.js' - -export default (): void => { - describe('batch', () => { - it('destination', async () => { - let process = 0 - let raw = 0 - const b = new DestinationBatch(0, { - process: (p, data) => { - if (p === undefined) p = [] - assert.equal(data.message, 'test') - p.push('test') - process++ - return p - }, - send: (data: string[]) => { - assert.equal(data.length, 2) - raw++ - } - }) - - const t = new Logger('test', { destination: b, logLevel: LogLevel.Error }) - - t?.error?.('test') - assert.equal(process, 1) - t?.error?.('test') - assert.equal(process, 2) - - assert.equal(raw, 0) - - await sleep(0) - - assert.equal(process, 2) - assert.equal(raw, 1) - }) - - it('flush', async () => { - let process = 0 - let raw = 0 - const b = new DestinationBatch(0, { - process: (p, data) => { - if (p === undefined) p = [] - assert.equal(data.message, 'test') - p.push('test') - process++ - return p - }, - send: (data: string[]) => { - assert.equal(data.length, 2) - raw++ - } - }) - - const t = new Logger('test', { destination: b, logLevel: LogLevel.Error }) - - t?.error?.('test') - assert.equal(process, 1) - t?.error?.('test') - assert.equal(process, 2) - - assert.equal(raw, 0) - - await t.flush() - - assert.equal(process, 2) - assert.equal(raw, 1) - - // @ts-expect-error workaround - b.run() - }) - }) -} diff --git a/packages/logasjson/test/context.ts b/packages/logasjson/test/context.ts deleted file mode 100644 index 32663e9..0000000 --- a/packages/logasjson/test/context.ts +++ /dev/null @@ -1,310 +0,0 @@ -import assert from 'assert/strict' -import { setTimeout as sleep } from 'timers/promises' - -import '../src/classes/Logger.js' - -import { Context } from '../src/classes/Context.js' - -export default (): void => { - describe('context', () => { - it('restart - storage undefined', async () => { - // @ts-expect-error workaround - Context.storage = undefined - // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression - assert.equal(Context.restart(), undefined) - }) - - it('getStore - storage undefined', async () => { - // @ts-expect-error workaround - Context.storage = undefined - assert.equal(Context.getStore(), undefined) - }) - - it('tracing', async () => { - Context.run({ name: 'test1' }, () => { - const a = Context.getStore() - - Context.run({ name: 'test2' }, () => { - const b = Context.getStore() - const c = Context.getStore() - assert.deepEqual(b, c) - assert.notDeepEqual(a, b) - }) - - const d = Context.getStore() - assert.deepEqual(a, d) - }) - - await Context.run({ name: 'test3' }, async () => { - const a = Context.getStore() - - await Context.run({ name: 'test3' }, async () => { - const b = Context.getStore() - await sleep(0) - const c = Context.getStore() - assert.deepEqual(b, c) - assert.notEqual(a, b) - }) - - await sleep(0) - - const d = Context.getStore() - assert.deepEqual(a, d) - }) - }) - - it('catch', async () => { - Context.runCatch({ name: 'test1' }, () => { - const a = Context.getStore() - Context.runCatch({ name: 'test2' }, () => { - throw new Error('test') - }) - const d = Context.getStore() - assert.deepEqual(a, d) - }) - - await Context.runCatch({ name: 'test3' }, async () => { - const a = Context.getStore() - - await Context.runCatch({ name: 'test3' }, async () => { - throw new Error('test') - }) - - const d = Context.getStore() - assert.deepEqual(a, d) - }) - - Context.runCatch({ name: 'test1' }, () => { - Context.run({ name: 'test2' }, () => { - throw new Error('test') - }) - assert.fail() - }) - - await Context.runCatch({ name: 'test3' }, async () => { - await Context.run({ name: 'test3' }, async () => { - throw new Error('test') - }) - assert.fail() - }) - }) - - it('store', async () => { - const store = Context.getStore() - assert.equal(store, undefined) - - await Context.run({ store: new Map([['a', 'b']]) }, async () => { - const store = Context.getStore() - if (store === undefined) assert.fail() - assert.equal(store.get('a', 'string'), 'b') - }) - - await Context.run({ name: 'test3', store: [['a', 'b']] }, async () => { - const store = Context.getStore() - if (store === undefined) assert.fail() - - assert.equal(store.get('a', 'string'), 'b') - - assert.equal(store.has('test'), false) - assert.equal(store.get('test', 'string'), undefined) - store.set('test', 'hallo') - assert.equal(store.has('test'), true) - assert.equal(store.get('test', 'string'), 'hallo') - - assert.throws(() => store.get('test', Number)) - assert.throws(() => store.get('test', 'number')) - assert.throws(() => store.get('test', String)) - assert.doesNotThrow(() => store.get('test', 'string')) - - const t = new Test2() - - store.set('test', t) - - assert.doesNotThrow(() => store.get('test', Test2)) - assert.doesNotThrow(() => store.get('test', Test)) - assert.throws(() => store.get('test', Test3)) - - assert.equal(store.getUnsafe('test'), t) - store.delete('test') - assert.equal(store.getUnsafe('test'), undefined) - }) - }) - - it('getOrSet', async () => { - const store = Context.getStore() - assert.equal(store, undefined) - - await Context.run({ store: new Map([['a', 'b']]) }, async () => { - const store = Context.getStore() - if (store === undefined) assert.fail() - - assert.equal(store.getOrSet('a', 'string', () => ''), 'b') - assert.equal(store.getOrSetUnsafe('a', () => ''), 'b') - - assert.equal(store.getOrSet('b', 'string', () => 'c'), 'c') - assert.equal(store.getOrSetUnsafe('c', () => 'd'), 'd') - }) - }) - - it('context inheritance', async () => { - const store = Context.getStore() - assert.equal(store, undefined) - - await Context.run({ name: 't1', store: new Map([['a', 'b'], ['c', 'e']]) }, async () => { - const store = Context.getStore() - if (store === undefined) assert.fail() - - const traceId = store.traceId - - await Context.run({ name: 't2', store: [['c', 'd']] }, async () => { - const store = Context.getStore() - if (store === undefined) assert.fail() - - assert.equal(store.name, 't1:t2') - - assert.equal(store.get('a', 'string'), undefined) - assert.equal(store.get('c', 'string'), 'd') - - assert.equal(store.traceId, traceId) - }) - }) - }) - - it('exit', async () => { - const store = Context.getStore() - assert.equal(store, undefined) - - await Context.run({ name: 't1', store: new Map([['a', 'b'], ['c', 'e']]) }, async () => { - const store = Context.getStore() - if (store === undefined) assert.fail() - - await Context.exit(async () => { - const store = Context.getStore() - if (store !== undefined) assert.fail() - }) - - await Context.exit(async () => { - await sleep(0) - const store = Context.getStore() - if (store !== undefined) assert.fail() - - await Context.run({ name: 't2' }, async () => { - const store = Context.getStore() - if (store === undefined) assert.fail() - if (store.name !== 't2') assert.fail() - }) - }) - - // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression - const t1 = await Context.exitCatch(async () => { - const store = Context.getStore() - if (store !== undefined) assert.fail() - throw new Error('test') - }) - assert.equal(t1, undefined) - - // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression - const t2 = Context.exitCatch(() => { - const store = Context.getStore() - if (store !== undefined) assert.fail() - throw new Error('test') - }) - assert.equal(t2, undefined) - - const t3 = await Context.exitCatch(async () => { - const store = Context.getStore() - if (store !== undefined) assert.fail() - return 'test' - }) - assert.equal(t3, 'test') - - const t4 = Context.exitCatch(() => { - const store = Context.getStore() - if (store !== undefined) assert.fail() - return 'test' - }) - assert.equal(t4, 'test') - }) - }) - - it('enterWith', async () => { - const store = Context.getStore() - assert.equal(store, undefined) - - await Context.run({ name: 't1', store: new Map([['a', 'b'], ['c', 'e']]) }, async () => { - let store = Context.getStore() - assert.equal(store?.name, 't1') - - await (async function () { - await sleep(0) - Context.enterWith({ name: 't2' }) - await sleep(0) - store = Context.getStore() - assert.equal(store?.name, 't1:t2') - await sleep(0) - store = Context.getStore() - assert.equal(store?.name, 't1:t2') - })() - - store = Context.getStore() - assert.equal(store?.name, 't1') - }) - }) - - it('inherit', async () => { - const store = Context.getStore() - assert.equal(store, undefined) - - await Context.run({ name: 't1', store: new Map([['a', 'b'], ['c', 'e']]) }, async () => { - const store = Context.getStore() - assert.equal(store?.name, 't1') - await Context.run({ name: 't2', inherit: false }, async () => { - const store = Context.getStore() - if (store === undefined) assert.fail() - assert.equal(store.name, 't2') - }) - }) - }) - - it('restart', async () => { - const store = Context.getStore() - assert.equal(store, undefined) - - await Context.run({ name: 't1', store: new Map([['a', 'b'], ['c', 'e']]) }, async () => { - Context.restart() - const store = Context.getStore() - assert.equal(store, undefined) - }) - - const t1 = Context.run({ name: 't1', store: new Map([['a', 'b'], ['c', 'e']]) }, async () => { - let store = Context.getStore() - assert.notEqual(store, undefined) - await sleep(0) - store = Context.getStore() - assert.equal(store, undefined) - }) - - Context.restart() - - await t1 - }) - }) - - class Test { - get (): string { - return 'test' - } - } - - class Test2 extends Test { - get (): string { - return 'test' - } - } - - class Test3 { - get (): string { - return 'test' - } - } -} diff --git a/packages/logasjson/test/filter.ts b/packages/logasjson/test/filter.ts deleted file mode 100644 index fcd6be7..0000000 --- a/packages/logasjson/test/filter.ts +++ /dev/null @@ -1,24 +0,0 @@ -import assert from 'assert/strict' - -import { LogLevel } from '../src/enums/loglevel.js' -import { filterLogLevel, filterTraceId } from '../src/helper/filter.js' - -export default (): void => { - describe('filter', () => { - it('filterTraceId', () => { - assert.equal(filterTraceId(undefined), undefined) - assert.equal(filterTraceId([]), undefined) - assert.equal(filterTraceId('sdsfs'), undefined) - assert.equal(filterTraceId('AAAAAAAA-AAAA-4AAA-8AAA-AAAAAAAAAAAA'), 'AAAAAAAA-AAAA-4AAA-8AAA-AAAAAAAAAAAA') - }) - - it('filterLogLevel', () => { - assert.equal(filterLogLevel(undefined), undefined) - assert.equal(filterLogLevel([]), undefined) - assert.equal(filterLogLevel('sdsfs'), undefined) - assert.equal(filterLogLevel('-1'), undefined) - assert.equal(filterLogLevel('5'), undefined) - assert.equal(filterLogLevel('4'), LogLevel.None) - }) - }) -} diff --git a/packages/logasjson/test/index.test.ts b/packages/logasjson/test/index.test.ts deleted file mode 100644 index 6e99533..0000000 --- a/packages/logasjson/test/index.test.ts +++ /dev/null @@ -1,19 +0,0 @@ -import batch from './batch.js' -import context from './context.js' -import filter from './filter.js' -import json from './json.js' -import logger from './logger.js' -import loki from './loki.js' -import lokireal from './lokireal.js' -import placeholder from './placeholder.js' - -describe('context', () => { - batch() - context() - filter() - json() - logger() - loki() - lokireal() - placeholder() -}) diff --git a/packages/logasjson/test/json.test.ts b/packages/logasjson/test/json.test.ts new file mode 100644 index 0000000..3a65ef5 --- /dev/null +++ b/packages/logasjson/test/json.test.ts @@ -0,0 +1,54 @@ +import assert from 'assert/strict' + +import { DestinationJson } from '../src/classes/destination/DestinationJson.js' +import { LogLevel } from '../src/enums/loglevel.js' +import { Logger } from '../src/classes/Logger.js' + +const stackTraceLimit = 20 +let oldValue = 0 + +describe('json', () => { + const write = process.stderr.write.bind(process.stderr) + + before(() => { + oldValue = Error.stackTraceLimit + + Error.stackTraceLimit = stackTraceLimit + + process.stderr.write = (...args: any) => { + const a = JSON.parse(args[0] as string) + assert.deepEqual(a?.error?.message, 'a') + const rows: string[] = a?.error?.stack?.split('\n') + assert.ok(rows.shift()?.includes('Error:') ?? false) + assert.deepEqual(rows.length, stackTraceLimit) + rows.forEach(e => { assert.ok(e.includes('at recursive')) }) + return write.apply(process.stdout, args as [str: string | Uint8Array, encoding?: BufferEncoding | undefined, cb?: ((err?: Error | undefined) => void) | undefined]) + } + }) + + after(() => { + Error.stackTraceLimit = oldValue + process.stderr.write = write + }) + + it('stringify error', () => { + const t = new Logger({ name: "test", destination: new DestinationJson(), logLevel: LogLevel.Info }) + + try { + recursive(stackTraceLimit) + } catch (error) { + const a: any = {} + a.t = a + a.error = new Error('b') + t.error?.('tester2', { error, a }) + } + + t.info?.("test") + }) +}) + + +function recursive(count: number): void { + if (count > 0) recursive(count - 1) + else throw new Error('a') +} diff --git a/packages/logasjson/test/json.ts b/packages/logasjson/test/json.ts deleted file mode 100644 index dbd968e..0000000 --- a/packages/logasjson/test/json.ts +++ /dev/null @@ -1,52 +0,0 @@ -import assert from 'assert/strict' - -import { DestinationJson } from '../src/classes/destination/DestinationJson.js' -import { Logger } from '../src/classes/Logger.js' -import { LogLevel } from '../src/enums/loglevel.js' - -const stackTraceLimit = 20 -let oldValue = 0 - -export default (): void => { - describe('json', () => { - const write = process.stderr.write.bind(process.stderr) - - before(() => { - oldValue = Error.stackTraceLimit - - Error.stackTraceLimit = stackTraceLimit - - process.stderr.write = (...args: any) => { - const a = JSON.parse(args[0] as string) - assert.deepEqual(a?.error?.message, 'a') - const rows: string[] = a?.error?.stack?.split('\n') - assert.ok(rows.shift()?.includes('Error:') ?? false) - assert.deepEqual(rows.length, stackTraceLimit) - rows.forEach(e => { assert.ok(e.includes('at recursive')) }) - return write.apply(process.stdout, args as [str: string | Uint8Array, encoding?: BufferEncoding | undefined, cb?: ((err?: Error | undefined) => void) | undefined]) - } - }) - - after(() => { - Error.stackTraceLimit = oldValue - process.stderr.write = write - }) - - it('stringify error', () => { - const log = new Logger('test', { destination: new DestinationJson(), logLevel: LogLevel.Error }) - try { - recursive(stackTraceLimit) - } catch (error) { - const a: any = { } - a.t = a - a.error = new Error('b') - log.error?.('tester2', { error, a }) - } - }) - }) -} - -function recursive (count: number): void { - if (count > 0) recursive(count - 1) - else throw new Error('a') -} diff --git a/packages/logasjson/test/logger.test.ts b/packages/logasjson/test/logger.test.ts new file mode 100644 index 0000000..982a202 --- /dev/null +++ b/packages/logasjson/test/logger.test.ts @@ -0,0 +1,144 @@ +/* eslint-disable @typescript-eslint/no-misused-promises */ +import assert from 'assert/strict' + +import { DestinationArray } from '../src/classes/destination/DestinationArray.js' +import { DestinationConsole } from '../src/classes/destination/DestinationConsole.js' +import { DestinationJson } from '../src/classes/destination/DestinationJson.js' +import { DestinationMessage } from '../src/classes/destination/DestinationMessage.js' +import { Logger } from '../src/classes/Logger.js' +import { LogLevel } from '../src/enums/loglevel.js' +import type { LoggerContext } from '../src/types/logger.js' +import { processLog } from '../src/helper/processLog.js' +import { setTimeout as sleep } from 'timers/promises' + +describe('logger', () => { + it('loglevel', async () => { + let context: null | LoggerContext = null + + const logger = new Logger({ name: 'test1', destination: new DestinationJson(), logLevel: LogLevel.Info, context: () => context ?? undefined }) + + const logger1 = logger.fork({ }) + assert.equal(logger1.debug, undefined) + assert.notEqual(logger1.info, undefined) + assert.notEqual(logger1.warn, undefined) + assert.notEqual(logger1.error, undefined) + + const logger2 = logger.fork({ logLevel: LogLevel.None }) + assert.equal(logger2.debug, undefined) + assert.equal(logger2.info, undefined) + assert.equal(logger2.warn, undefined) + assert.equal(logger2.error, undefined) + + context = { name: "test2", logLevel: LogLevel.Error } + assert.equal(logger2.debug, undefined) + assert.equal(logger2.info, undefined) + assert.equal(logger2.warn, undefined) + assert.notEqual(logger2.error, undefined) + + + context = { name: "test", logLevel: LogLevel.Warn } + assert.equal(logger2.debug, undefined) + assert.equal(logger2.info, undefined) + assert.notEqual(logger2.warn, undefined) + assert.notEqual(logger2.error, undefined) + + context = { name: "test", logLevel: LogLevel.Debug } + assert.notEqual(logger2.debug, undefined) + assert.notEqual(logger2.info, undefined) + assert.notEqual(logger2.warn, undefined) + assert.notEqual(logger2.error, undefined) + }) + + it('write', async () => { + let called1 = false + + + let context: null | LoggerContext = null + + const logger = new Logger({ + name: 'test', + destination: { + write: (data) => { + called1 = true + assert.equal(data.logLevel, LogLevel.Warn) + assert.equal(data.logger, 'test') + assert.equal(data.message, 'testMessage hallo') + assert.equal(data.hi, 'hallo') + assert.notEqual(data.timestamp, undefined) + assert.equal(data.traceId, "hi") + assert.equal(data.context, 'testcontext') + } + }, + context: () => context ?? undefined, + logLevel: LogLevel.Info + }) + + context = { name: 'testcontext', traceId: "hi" } + logger.warn?.('testMessage :hi', { hi: 'hallo' }) + context = null + + assert.equal(called1, true) + + let called2 = false + + const da = new DestinationArray([{ + write: (data) => { + assert.equal(data.logLevel, LogLevel.Warn) + assert.equal(data.message, 'hi') + called2 = true + }, + async flush(): Promise { + await sleep(100) + } + }]) + + context = { name: 'testcontext' } + + const logger2 = new Logger({ destination: da, logLevel: LogLevel.Info }) + logger2.warn?.('hi') + await da.flush() + assert.equal(called2, true) + + const message = new DestinationMessage() + + message.write({ message: 'test1', logLevel: LogLevel.Debug, timestamp: Date.now() }) + message.write({ message: 'test2', logLevel: LogLevel.Error, timestamp: Date.now() }) + message.write({ message: '5', logLevel: LogLevel.None, timestamp: Date.now() }) + + message.write({ message: 'test2', error: new Error('t1'), logLevel: LogLevel.Warn, timestamp: Date.now() }) + message.write({ message: 'test2', error: new Error('t2'), logLevel: LogLevel.Error, timestamp: Date.now() }) + + const dconsole = new DestinationConsole() + + dconsole.write({ message: 'test1', logLevel: LogLevel.Debug, timestamp: Date.now() }) + dconsole.write({ message: 'test2', logLevel: LogLevel.Error, timestamp: Date.now() }) + dconsole.write({ message: '5', logLevel: LogLevel.None, timestamp: Date.now() }) + + dconsole.write({ message: 'test2', error: new Error('t1'), logLevel: LogLevel.Warn, timestamp: Date.now() }) + dconsole.write({ message: 'test2', error: new Error('t2'), logLevel: LogLevel.Error, timestamp: Date.now() }) + }) + + it('processData', async () => { + const d = processLog(undefined, LogLevel.Debug, 'test', {}, 'test2', {}, false) + assert.equal(d.logger, 'test') + }) + + it('traceId', async () => { + let context: null | LoggerContext = null + const log = new Logger({ logLevel: LogLevel.Debug, context: () => context ?? undefined }) + assert.equal(log.traceId, undefined) + context = { traceId: "hi"} + assert.equal(log.traceId, "hi") + }) + + it('stacktrace', async () => { + const log = new Logger({ logLevel: LogLevel.Debug }) + log.info?.("hi", undefined, true) + }) + + it('flush', async () => { + const log1 = new Logger({ name: 'str1', logLevel: LogLevel.Info }) + + await log1.flush() + }) +}) diff --git a/packages/logasjson/test/logger.ts b/packages/logasjson/test/logger.ts deleted file mode 100644 index efe8969..0000000 --- a/packages/logasjson/test/logger.ts +++ /dev/null @@ -1,252 +0,0 @@ -/* eslint-disable @typescript-eslint/no-misused-promises */ -import assert from 'assert/strict' - -import { Context } from '../src/classes/Context.js' -import { DestinationArray } from '../src/classes/destination/DestinationArray.js' -import { DestinationConsole } from '../src/classes/destination/DestinationConsole.js' -import { DestinationJson } from '../src/classes/destination/DestinationJson.js' -import { DestinationMessage } from '../src/classes/destination/DestinationMessage.js' -import { Logger } from '../src/classes/Logger.js' -import { LogLevel } from '../src/enums/loglevel.js' -import { filterTraceId } from '../src/helper/filter.js' - -export default (): void => { - describe('logger', () => { - it('loglevel', async () => { - Logger.options.logLevel = LogLevel.Info - - let logger = new Logger('test1') - assert.equal(logger.debug, undefined) - assert.notEqual(logger.info, undefined) - assert.notEqual(logger.warn, undefined) - assert.notEqual(logger.error, undefined) - - Logger.options.logLevel = undefined - - assert.equal(logger.debug, undefined) - assert.equal(logger.info, undefined) - assert.equal(logger.warn, undefined) - assert.equal(logger.error, undefined) - - Context.run({ name: 'test2', logger: { logLevel: LogLevel.Error } }, () => { - assert.equal(logger.debug, undefined) - assert.equal(logger.info, undefined) - assert.equal(logger.warn, undefined) - assert.notEqual(logger.error, undefined) - }) - - Logger.options.logLevel = LogLevel.Warn - assert.equal(logger.debug, undefined) - assert.equal(logger.info, undefined) - assert.notEqual(logger.warn, undefined) - assert.notEqual(logger.error, undefined) - - logger = new Logger('test2', { logLevel: LogLevel.Debug }) - assert.notEqual(logger.debug, undefined) - assert.notEqual(logger.info, undefined) - assert.notEqual(logger.warn, undefined) - assert.notEqual(logger.error, undefined) - - assert.deepEqual(logger.logLevel, LogLevel.Debug) - - Context.run({ name: 'test2', logger: { logLevel: LogLevel.Error } }, () => { - assert.deepEqual(logger.logLevel, LogLevel.Error) - - assert.equal(logger.debug, undefined) - assert.equal(logger.info, undefined) - assert.equal(logger.warn, undefined) - assert.notEqual(logger.error, undefined) - }) - - Context.run({ name: 'test2' }, () => { - assert.notEqual(logger.debug, undefined) - assert.notEqual(logger.info, undefined) - assert.notEqual(logger.warn, undefined) - assert.notEqual(logger.error, undefined) - }) - - logger = new Logger('test3', { logLevel: LogLevel.Error }) - assert.equal(logger.debug, undefined) - assert.equal(logger.info, undefined) - assert.equal(logger.warn, undefined) - assert.notEqual(logger.error, undefined) - - Context.run({ name: 'test3', logger: { logLevel: LogLevel.Debug } }, () => { - assert.notEqual(logger.debug, undefined) - assert.notEqual(logger.info, undefined) - assert.notEqual(logger.warn, undefined) - assert.notEqual(logger.error, undefined) - }) - - Logger.options.logLevel = LogLevel.Info - }) - - it('write', async () => { - let called1 = false - const logger = new Logger('test', { - destination: { - write: (data) => { - called1 = true - assert.equal(data.logLevel, LogLevel.Warn) - assert.equal(data.logger, 'test') - assert.equal(data.message, 'testMessage hallo') - assert.equal(data.hi, 'hallo') - assert.notEqual(data.timestamp, undefined) - assert.notEqual(data.traceId, undefined) - assert.equal(data.context, 'testcontext') - } - } - }) - - Context.run({ name: 'testcontext' }, () => { - logger.warn?.('testMessage :hi', { hi: 'hallo' }) - }) - - assert.equal(called1, true) - - let called2 = false - - const da = new DestinationArray([{ - write: (data) => { - assert.equal(data.logLevel, LogLevel.Warn) - assert.equal(data.message, 'hi') - called2 = true - } - }]) - - Context.run({ - name: 'testcontext', - logger: { - destination: da - } - }, () => { - logger.warn?.('hi') - }) - await da.flush() - assert.equal(called2, true) - - let log = new Logger('test') - log.warn?.('test1', {}, true) - log.error?.('test2', {}, false) - - log = new Logger('test', { destination: new DestinationJson() }) - log.warn?.('test1', {}, false) - log.error?.('test2', {}, false) - - const message = new DestinationMessage() - - message.write({ message: 'test1', logLevel: LogLevel.Debug, timestamp: Date.now() }) - message.write({ message: 'test2', logLevel: LogLevel.Error, timestamp: Date.now() }) - message.write({ message: '5', logLevel: LogLevel.None, timestamp: Date.now() }) - - message.write({ message: 'test2', error: new Error('t1'), logLevel: LogLevel.Warn, timestamp: Date.now() }) - message.write({ message: 'test2', error: new Error('t2'), logLevel: LogLevel.Error, timestamp: Date.now() }) - - const console = new DestinationConsole() - - console.write({ message: 'test1', logLevel: LogLevel.Debug, timestamp: Date.now() }) - console.write({ message: 'test2', logLevel: LogLevel.Error, timestamp: Date.now() }) - console.write({ message: '5', logLevel: LogLevel.None, timestamp: Date.now() }) - - console.write({ message: 'test2', error: new Error('t1'), logLevel: LogLevel.Warn, timestamp: Date.now() }) - console.write({ message: 'test2', error: new Error('t2'), logLevel: LogLevel.Error, timestamp: Date.now() }) - }) - - it('extend', async () => { - let called = false - const log1 = new Logger('test1', { logLevel: LogLevel.Debug, data: { hi: 'hi' } }) - const log2 = log1.extend('test2', { - data: { ho: 'ho' }, - destination: { - write: (data) => { - called = true - - assert.equal(data.hi, 'hi') - assert.equal(data.ho, 'ho') - assert.equal(data.test, 'test') - } - } - }) - - assert.equal(log1.name, 'test1') - assert.equal(log1.logLevel, LogLevel.Debug) - assert.equal(log2.name, 'test1:test2') - assert.equal(log2.logLevel, LogLevel.Debug) - - log2.info?.('test', { test: 'test' }) - assert.equal(called, true) - - const log3 = new Logger() - const log4 = log3.extend() - - assert.equal(log4.name, ':') - }) - - it('override.setByLogger', async () => { - const log1 = new Logger('str', { logLevel: LogLevel.Info }) - assert.equal(log1.logLevel, LogLevel.Info) - Logger.override.setByLogger(LogLevel.Error, 'str') - assert.equal(log1.logLevel, LogLevel.Error) - Logger.override.deleteByLogger('str') - assert.equal(log1.logLevel, LogLevel.Info) - Logger.override.setByLogger(LogLevel.Error, 'str2') - assert.equal(log1.logLevel, LogLevel.Info) - Logger.override.clear() - }) - - it('override.setByContext', async () => { - const log1 = new Logger('str1', { logLevel: LogLevel.Info }) - - Context.run({ - name: 'str2' - }, () => { - assert.equal(log1.logLevel, LogLevel.Info) - Logger.override.setByContext(LogLevel.Error, 'str2') - assert.equal(log1.logLevel, LogLevel.Error) - Logger.override.deleteByContext('str2') - assert.equal(log1.logLevel, LogLevel.Info) - Logger.override.setByContext(LogLevel.Error, 'str') - assert.equal(log1.logLevel, LogLevel.Info) - Logger.override.clear() - }) - }) - - it('override.set', async () => { - const log1 = new Logger('str1', { logLevel: LogLevel.Info }) - - Context.run({ - name: 'str2' - }, () => { - assert.equal(log1.logLevel, LogLevel.Info) - Logger.override.set(LogLevel.Error, 'str1', 'str2') - assert.equal(log1.logLevel, LogLevel.Error) - Logger.override.delete('str1', 'str2') - assert.equal(log1.logLevel, LogLevel.Info) - Logger.override.set(LogLevel.Error, 'str2', 'str1') - assert.equal(log1.logLevel, LogLevel.Info) - Logger.override.clear() - }) - }) - - it('processData', async () => { - let d = Logger.processData(LogLevel.Debug, 'test', {}, 'test2', {}, false) - assert.equal(d.logger, 'test') - - // @ts-expect-error workaround - d = Logger.processData(LogLevel.Debug, 'test', {}, undefined, {}, true) - assert.equal(d.message, 'undefined') - }) - - it('traceId', async () => { - const d = (new Logger()).traceId - assert.equal(filterTraceId(d), d) - }) - - it('flush', async () => { - const log1 = new Logger('str1', { logLevel: LogLevel.Info }) - - await log1.flush() - await Logger.flush() - }) - }) -} diff --git a/packages/logasjson/test/loki.test.ts b/packages/logasjson/test/loki.test.ts new file mode 100644 index 0000000..f5f95c5 --- /dev/null +++ b/packages/logasjson/test/loki.test.ts @@ -0,0 +1,185 @@ +/* eslint-disable @typescript-eslint/no-empty-function */ +/* eslint-disable @typescript-eslint/no-misused-promises */ +import assert from 'assert/strict' + +import nock from 'nock' + +import { DestinationLoki } from '../src/classes/destination/DestinationLoki.js' +import { Logger } from '../src/classes/Logger.js' +import { LogLevel } from '../src/enums/loglevel.js' +import type { LoggerContext } from '../src/types/logger.js' +import { setTimeout as sleep } from 'timers/promises' + +describe('loki', () => { + it('prepare', async () => { + const loki = new DestinationLoki({ + host: 'http://localhost:1', + fallback: { + write: () => { } + } + }) + const d = loki.process(undefined, { + timestamp: Date.now(), + logLevel: LogLevel.Debug, + message: 'test1' + }) + + loki.process(d, { + timestamp: Date.now(), + logLevel: LogLevel.Debug, + message: 'test2' + }) + }) + + it('write', async () => { + const called = [0, 0, 0, 0] + + const loki1 = new DestinationLoki({ + host: 'http://localhost:1', + fallback: { + write: () => { + called[0]++ + } + }, + compression: true, + authentication: { + username: 'test', + password: 'test' + } + }) + const loki2 = new DestinationLoki({ + host: 'http://localhost', + fallback: { + write: () => { + called[1]++ + }, + async flush() { + await sleep(100) + } + }, + compression: true + }) + const loki3 = new DestinationLoki({ + host: 'https://localhost:2', + fallback: { + write: () => { + called[2]++ + } + } + }) + const loki4 = new DestinationLoki({ + host: 'https://localhost', + fallback: { + write: () => { + called[3]++ + } + } + }) + + nock('http://localhost:1') + .post('/loki/api/v1/push') + .times(4) + .reply(200) + + nock('http://localhost') + .post('/loki/api/v1/push') + .times(3) + .reply(204) + + nock('https://localhost:2') + .post('/loki/api/v1/push') + .times(2) + .reply(500, 'error') + + nock('https://localhost') + .post('/loki/api/v1/push') + .times(2) + .reply(204) + + let context: null | LoggerContext = null + + const logger = new Logger({ name: 'test', destination: loki1, logLevel: LogLevel.Debug, context: () => context ?? undefined }) + + logger.error?.('loggertest', { hallo: 'test1' }) + + context = { name: 'contexttest' } + logger.warn?.('loggertest', { hi: 'test2' }) + context = null + + loki2.write({ + timestamp: Date.now(), + logLevel: LogLevel.Debug, + message: 'test1' + }) + + await loki1.flush() + + + const logger2 = new Logger({ name: 'test', destination: loki2, logLevel: LogLevel.Debug, context: () => context ?? undefined }) + logger2.error?.('loggertest', { hallo: 'test1' }) + context = { name: 'contexttest' } + logger2.warn?.('loggertest', { hi: 'test2' }) + context = null + await loki2.flush() + + const logger3 = new Logger({ name: 'test', destination: loki3, logLevel: LogLevel.Debug, context: () => context ?? undefined }) + logger3.error?.('loggertest', { hallo: 'test1' }) + context = { name: 'contexttest' } + logger3.warn?.('loggertest', { hi: 'test2' }) + context = null + await loki3.flush() + + const logger4 = new Logger({ name: 'test', destination: loki4, logLevel: LogLevel.Debug, context: () => context ?? undefined }) + logger4.error?.('loggertest', { hallo: 'test1' }) + context = { name: 'contexttest' } + logger4.warn?.('loggertest', { hi: 'test2' }) + context = null + await loki4.flush() + + assert.deepEqual(called, [2, 0, 2, 0]) + + // eslint-disable-next-line import/no-named-as-default-member + nock.cleanAll() + + // Missing code paths: + loki2.write({ + timestamp: Date.now(), + logLevel: LogLevel.None, + message: 'test1' + }) + + loki2.write({ + timestamp: Date.now(), + logLevel: LogLevel.Info, + message: 'test1' + }) + }) + + it('zlib error', async () => { + let called = 0 + const loki2 = new DestinationLoki({ + host: 'http://localhost', + fallback: { + write: (e) => { + called++ + assert.equal(e.message, 'Cannot create a Buffer larger than 64 bytes') + } + }, + compression: true, + zliboptions: { + maxOutputLength: 64 + } + }) + + loki2.write({ + timestamp: Date.now(), + logLevel: LogLevel.Debug, + message: 'test1' + }) + + await loki2.flush() + + assert.equal(called, 1) + }) +}) + diff --git a/packages/logasjson/test/loki.ts b/packages/logasjson/test/loki.ts deleted file mode 100644 index f3a0ac3..0000000 --- a/packages/logasjson/test/loki.ts +++ /dev/null @@ -1,187 +0,0 @@ -/* eslint-disable @typescript-eslint/no-empty-function */ -/* eslint-disable @typescript-eslint/no-misused-promises */ -import assert from 'assert/strict' - -import nock from 'nock' - -import { Context } from '../src/classes/Context.js' -import { DestinationLoki } from '../src/classes/destination/DestinationLoki.js' -import { Logger } from '../src/classes/Logger.js' -import { LogLevel } from '../src/enums/loglevel.js' - -export default (): void => { - describe('loki', () => { - it('prepare', async () => { - const loki = new DestinationLoki({ - host: 'http://localhost:1', - fallback: { - write: () => {} - } - }) - const d = loki.process(undefined, { - timestamp: Date.now(), - logLevel: LogLevel.Debug, - message: 'test1' - }) - - loki.process(d, { - timestamp: Date.now(), - logLevel: LogLevel.Debug, - message: 'test2' - }) - }) - - it('write', async () => { - Logger.options.logLevel = LogLevel.Debug - - const called = [0, 0, 0, 0] - - const loki1 = new DestinationLoki({ - host: 'http://localhost:1', - fallback: { - write: () => { - called[0]++ - } - }, - compression: true, - authentication: { - username: 'test', - password: 'test' - } - }) - const loki2 = new DestinationLoki({ - host: 'http://localhost', - fallback: { - write: () => { - called[1]++ - } - }, - compression: true - }) - const loki3 = new DestinationLoki({ - host: 'https://localhost:2', - fallback: { - write: () => { - called[2]++ - } - } - }) - const loki4 = new DestinationLoki({ - host: 'https://localhost', - fallback: { - write: () => { - called[3]++ - } - } - }) - - nock('http://localhost:1') - .post('/loki/api/v1/push') - .times(4) - .reply(200) - - nock('http://localhost') - .post('/loki/api/v1/push') - .times(3) - .reply(204) - - nock('https://localhost:2') - .post('/loki/api/v1/push') - .times(2) - .reply(500, 'error') - - nock('https://localhost') - .post('/loki/api/v1/push') - .times(2) - .reply(204) - - Logger.options.destination = loki1 - - const logger = new Logger('test') - - logger.error?.('loggertest', { hallo: 'test1' }) - - Context.run({ - name: 'contexttest' - }, () => { - logger.warn?.('loggertest', { hi: 'test2' }) - }) - - loki2.write({ - timestamp: Date.now(), - logLevel: LogLevel.Debug, - message: 'test1' - }) - - await loki1.flush() - - Logger.options.destination = loki2 - - logger.error?.('loggertest', { hallo: 'test1' }) - - Context.run({ - name: 'contexttest' - }, () => { - logger.warn?.('loggertest', { hi: 'test2' }) - }) - - await loki2.flush() - - Logger.options.destination = loki3 - - logger.error?.('loggertest', { hallo: 'test1' }) - - Context.run({ - name: 'contexttest' - }, () => { - logger.warn?.('loggertest', { hi: 'test2' }) - }) - - await loki3.flush() - - Logger.options.destination = loki4 - - logger.error?.('loggertest', { hallo: 'test1' }) - - Context.run({ - name: 'contexttest' - }, () => { - logger.warn?.('loggertest', { hi: 'test2' }) - }) - - await loki4.flush() - - assert.deepEqual(called, [2, 0, 2, 0]) - - // eslint-disable-next-line import/no-named-as-default-member - nock.cleanAll() - }) - - it('zlib error', async () => { - let called = 0 - const loki2 = new DestinationLoki({ - host: 'http://localhost', - fallback: { - write: (e) => { - called++ - assert.equal(e.message, 'Cannot create a Buffer larger than 64 bytes') - } - }, - compression: true, - zliboptions: { - maxOutputLength: 64 - } - }) - - loki2.write({ - timestamp: Date.now(), - logLevel: LogLevel.Debug, - message: 'test1' - }) - - await loki2.flush() - - assert.equal(called, 1) - }) - }) -} diff --git a/packages/logasjson/test/lokireal.ts b/packages/logasjson/test/lokireal.test.ts similarity index 70% rename from packages/logasjson/test/lokireal.ts rename to packages/logasjson/test/lokireal.test.ts index ee7a6bb..f329e84 100644 --- a/packages/logasjson/test/lokireal.ts +++ b/packages/logasjson/test/lokireal.test.ts @@ -2,15 +2,17 @@ import { DestinationConsole } from '../src/classes/destination/DestinationConsole.js' import { DestinationLoki } from '../src/classes/destination/DestinationLoki.js' import { Logger } from '../src/classes/Logger.js' +import { LogLevel } from '../src/enums/loglevel.js' export default (): void => { describe.skip('lokireal', () => { it('test', async () => { const loki = new DestinationLoki({ host: 'http://localhost:3100', fallback: new DestinationConsole(), labels: { env: 'test' } }) - const logger = new Logger('test', { destination: loki }) - logger.error?.('loggertest', { hallo: 'test1' }) - await logger.flush() + const t = new Logger({ name: 'test', destination: loki, logLevel: LogLevel.Error, }) + t.error?.('loggertest', { hallo: 'test1' }) + + await t.flush() }) }) } diff --git a/packages/logasjson/test/override.test.ts b/packages/logasjson/test/override.test.ts new file mode 100644 index 0000000..21d9ac0 --- /dev/null +++ b/packages/logasjson/test/override.test.ts @@ -0,0 +1,64 @@ +/* eslint-disable @typescript-eslint/no-misused-promises */ +import assert from 'assert/strict' + +import { Logger } from '../src/classes/Logger.js' +import { LogLevel } from '../src/enums/loglevel.js' +import type { LoggerContext } from '../src/types/logger.js' +import { Override } from '../src/classes/Override.js' + +describe('override', () => { + + it('setByLogger', async () => { + const log1 = new Logger({ name: 'str', logLevel: LogLevel.Info }) + assert.equal(log1.logLevel, LogLevel.Info) + log1.override.setByLogger(LogLevel.Error, 'str') + assert.equal(log1.logLevel, LogLevel.Error) + log1.override.deleteByLogger('str') + assert.equal(log1.logLevel, LogLevel.Info) + log1.override.setByLogger(LogLevel.Error, 'str2') + assert.equal(log1.logLevel, LogLevel.Info) + log1.override.clear() + }) + + it('setByContext', async () => { + let context: null | LoggerContext = null + + const log1 = new Logger({ name: 'str', logLevel: LogLevel.Info, context: () => context ?? undefined }) + + context = { name: 'str2' } + + assert.equal(log1.logLevel, LogLevel.Info) + log1.override.setByContext(LogLevel.Error, 'str2') + assert.equal(log1.logLevel, LogLevel.Error) + log1.override.deleteByContext('str2') + assert.equal(log1.logLevel, LogLevel.Info) + log1.override.setByContext(LogLevel.Error, 'str') + assert.equal(log1.logLevel, LogLevel.Info) + log1.override.clear() + }) + + it('set', async () => { + let context: null | LoggerContext = null + + const log1 = new Logger({ name: 'str1', logLevel: LogLevel.Info, context: () => context ?? undefined }) + + context = { name: 'str2' } + assert.equal(log1.logLevel, LogLevel.Info) + log1.override.set(LogLevel.Error, 'str1', 'str2') + assert.equal(log1.logLevel, LogLevel.Error) + log1.override.delete('str1', 'str2') + assert.equal(log1.logLevel, LogLevel.Info) + log1.override.set(LogLevel.Error, 'str2', 'str1') + assert.equal(log1.logLevel, LogLevel.Info) + log1.override.clear() + + }) + + + it('data', async () => { + const override = new Override() + override.set(LogLevel.Error, 'str1', 'str2') + assert.deepEqual(override.data, new Map([['["str2","str1"]', LogLevel.Error]])) + }) + +}) diff --git a/packages/logasjson/test/placeholder.test.ts b/packages/logasjson/test/placeholder.test.ts new file mode 100644 index 0000000..6f85df0 --- /dev/null +++ b/packages/logasjson/test/placeholder.test.ts @@ -0,0 +1,23 @@ +import assert from 'assert/strict' + +import { placeholder } from '../src/helper/placeholder.js' + +describe('placeholder', () => { + it('placeholder', () => { + let res = placeholder('test :user :userId', { user: 'a', userId: 'b' }) + assert.equal(res, 'test a b') + res = placeholder('test :user :user2Id', { user: 'a', userId: 'b' }) + assert.equal(res, 'test a :user2Id') + res = placeholder('test :user :user.id :user.test.a.b', { user: { id: 'test' }, userId: 'b' }) + assert.equal(res, 'test [object Object] test :user.test.a.b') + res = placeholder('test : test', { user: { id: 'test' }, userId: 'b' }) + assert.equal(res, 'test : test') + res = placeholder('test :user.length test', { user: ['a'] }) + assert.equal(res, 'test 1 test') + + + res = placeholder(5 as any as string, { user: ['a'] }) + assert.equal(res, '5') + }) +}) + diff --git a/packages/logasjson/test/placeholder.ts b/packages/logasjson/test/placeholder.ts deleted file mode 100644 index 1604ada..0000000 --- a/packages/logasjson/test/placeholder.ts +++ /dev/null @@ -1,20 +0,0 @@ -import assert from 'assert/strict' - -import { placeholder } from '../src/helper/placeholder.js' - -export default (): void => { - describe('placeholder', () => { - it('placeholder', () => { - let res = placeholder('test :user :userId', { user: 'a', userId: 'b' }) - assert.equal(res, 'test a b') - res = placeholder('test :user :user2Id', { user: 'a', userId: 'b' }) - assert.equal(res, 'test a :user2Id') - res = placeholder('test :user :user.id :user.test.a.b', { user: { id: 'test' }, userId: 'b' }) - assert.equal(res, 'test [object Object] test :user.test.a.b') - res = placeholder('test : test', { user: { id: 'test' }, userId: 'b' }) - assert.equal(res, 'test : test') - res = placeholder('test :user.length test', { user: ['a'] }) - assert.equal(res, 'test 1 test') - }) - }) -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 73f7900..e7a5757 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -24,7 +24,7 @@ importers: specifier: ^10.7.0 version: 10.7.0 typedoc: - specifier: ^0.26.3 + specifier: ^0.26.5 version: 0.26.5(typescript@5.5.4) typescript: specifier: ^5.5.4 @@ -122,8 +122,8 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@shikijs/core@1.11.1': - resolution: {integrity: sha512-Qsn8h15SWgv5TDRoDmiHNzdQO2BxDe86Yq6vIHf5T0cCvmfmccJKIzHtep8bQO9HMBZYCtCBzaXdd1MnxZBPSg==} + '@shikijs/core@1.12.0': + resolution: {integrity: sha512-mc1cLbm6UQ8RxLc0dZES7v5rkH+99LxQp/ZvTqV3NLyYsO/fD6JhEflP1H5b2SDq9gI0+0G36AVZWxvounfR9w==} '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} @@ -373,8 +373,8 @@ packages: supports-color: optional: true - debug@4.3.5: - resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} + debug@4.3.6: + resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -517,8 +517,8 @@ packages: '@typescript-eslint/parser': optional: true - eslint-plugin-n@17.9.0: - resolution: {integrity: sha512-CPSaXDXdrT4nsrOrO4mT4VB6FMUkoySRkHWuuJJHVqsIEjIeZgMY1H7AzSwPbDScikBmLN82KeM1u7ixV7PzGg==} + eslint-plugin-n@17.10.1: + resolution: {integrity: sha512-hm/q37W6efDptJXdwirsm6A257iY6ZNtpoSG0wEzFzjJ3AhL7OhEIhdSR2e4OdYfHO5EDeqlCfFrjf9q208IPw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: '>=8.23.0' @@ -1145,8 +1145,8 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - shiki@1.11.1: - resolution: {integrity: sha512-VHD3Q0EBXaaa245jqayBe5zQyMQUdXBFjmGr9MpDaDpAKRMYn7Ff00DM5MLk26UyKjnml3yQ0O2HNX7PtYVNFQ==} + shiki@1.12.0: + resolution: {integrity: sha512-BuAxWOm5JhRcbSOl7XCei8wGjgJJonnV0oipUupPY58iULxUGyHhW5CF+9FRMuM1pcJ5cGEJGll1LusX6FwpPA==} side-channel@1.0.6: resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} @@ -1374,7 +1374,7 @@ snapshots: '@eslint/eslintrc@2.1.4': dependencies: ajv: 6.12.6 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) espree: 9.6.1 globals: 13.24.0 ignore: 5.3.1 @@ -1390,7 +1390,7 @@ snapshots: '@humanwhocodes/config-array@0.11.14': dependencies: '@humanwhocodes/object-schema': 2.0.3 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -1434,7 +1434,7 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@shikijs/core@1.11.1': + '@shikijs/core@1.12.0': dependencies: '@types/hast': 3.0.4 @@ -1480,7 +1480,7 @@ snapshots: '@typescript-eslint/types': 7.17.0 '@typescript-eslint/typescript-estree': 7.17.0(typescript@5.5.4) '@typescript-eslint/visitor-keys': 7.17.0 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) eslint: 8.57.0 optionalDependencies: typescript: 5.5.4 @@ -1496,7 +1496,7 @@ snapshots: dependencies: '@typescript-eslint/typescript-estree': 7.17.0(typescript@5.5.4) '@typescript-eslint/utils': 7.17.0(eslint@8.57.0)(typescript@5.5.4) - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) eslint: 8.57.0 ts-api-utils: 1.3.0(typescript@5.5.4) optionalDependencies: @@ -1510,7 +1510,7 @@ snapshots: dependencies: '@typescript-eslint/types': 7.17.0 '@typescript-eslint/visitor-keys': 7.17.0 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.5 @@ -1737,7 +1737,7 @@ snapshots: dependencies: ms: 2.1.3 - debug@4.3.5(supports-color@8.1.1): + debug@4.3.6(supports-color@8.1.1): dependencies: ms: 2.1.2 optionalDependencies: @@ -1874,7 +1874,7 @@ snapshots: dependencies: eslint: 8.57.0 eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.17.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0) - eslint-plugin-n: 17.9.0(eslint@8.57.0) + eslint-plugin-n: 17.10.1(eslint@8.57.0) eslint-plugin-promise: 6.6.0(eslint@8.57.0) typescript: 5.5.4 typescript-eslint: 7.17.0(eslint@8.57.0)(typescript@5.5.4) @@ -1936,7 +1936,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-n@17.9.0(eslint@8.57.0): + eslint-plugin-n@17.10.1(eslint@8.57.0): dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) enhanced-resolve: 5.17.1 @@ -1972,7 +1972,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.2.2 @@ -2405,7 +2405,7 @@ snapshots: ansi-colors: 4.1.3 browser-stdout: 1.3.1 chokidar: 3.6.0 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) diff: 5.2.0 escape-string-regexp: 4.0.0 find-up: 5.0.0 @@ -2431,7 +2431,7 @@ snapshots: nock@13.5.4: dependencies: - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) json-stringify-safe: 5.0.1 propagate: 2.0.1 transitivePeerDependencies: @@ -2613,9 +2613,9 @@ snapshots: shebang-regex@3.0.0: {} - shiki@1.11.1: + shiki@1.12.0: dependencies: - '@shikijs/core': 1.11.1 + '@shikijs/core': 1.12.0 '@types/hast': 3.0.4 side-channel@1.0.6: @@ -2752,7 +2752,7 @@ snapshots: lunr: 2.3.9 markdown-it: 14.1.0 minimatch: 9.0.5 - shiki: 1.11.1 + shiki: 1.12.0 typescript: 5.5.4 yaml: 2.5.0