Comparing version 1.1.0-beta.0 to 1.1.0-beta.1
@@ -10,10 +10,114 @@ /** | ||
/** | ||
* Gets or creates a logger by name | ||
* Gets or creates a logger by name. | ||
* | ||
* Acts as a store for all loggers created so far, | ||
* as a factory to create new loggers and as an | ||
* adapter (the no-op adapter) to extend the loggers. | ||
* | ||
* Anylogger has the concept of levels and it can be | ||
* used as a delegate to log with a certain logger. | ||
*/ | ||
export type AnyLogger = ((name: LoggerName) => Logger) & { | ||
export type AnyLogger = GetLogger & HasStore & HasFactory & HasExtension & HasLevels & HasDelegate; | ||
/** | ||
* Gets a logger by `name`. | ||
*/ | ||
export type GetLogger = (name: string) => Logger; | ||
/** | ||
* A logger is a log function that has a `name` that corresponds to the logger | ||
* name, a method `enabledFor(level: LogLevel)` to check whether the logger is | ||
* enabled for a certain log level, and log methods for each of the log levels | ||
* supported by AnyLogger: `error`, `warn`, `info`, `log`, `debug` and `trace`. | ||
*/ | ||
export type Logger = LogFunction & LogObject & { | ||
readonly name: string; | ||
enabledFor: (level?: LogLevel) => boolean | void; | ||
}; | ||
/** | ||
* A log function is a function that takes a variable amount of | ||
* arguments and returns void. | ||
*/ | ||
export type LogFunction = (...args: any) => void; | ||
/** | ||
* A log object is an object that has methods corresponding to all | ||
* supported log levels, where each method is a log function. | ||
*/ | ||
export type LogObject = { | ||
[P in keyof LogLevels as `${P}`]: LogFunction; | ||
}; | ||
declare const anylogger: AnyLogger; | ||
/** | ||
* Stores all loggers created so far | ||
*/ | ||
export type HasStore = { | ||
all: AllLoggers; | ||
}; | ||
/** | ||
* All loggers, keyed by name | ||
*/ | ||
export type AllLoggers = { | ||
[name: string]: Logger; | ||
}; | ||
/** | ||
* Anylogger creates new loggers when needed | ||
*/ | ||
export type HasFactory = { | ||
/** | ||
* Stores all loggers created so far | ||
* Called when a new logger needs to be created. | ||
* | ||
* The default implementation creates a log function that | ||
* allows for an optional first argument, preceding the | ||
* message argument(s), to specify the level to call. | ||
* | ||
* @param name The name for the new logger | ||
* @returns The newly created logger | ||
*/ | ||
all: AllLoggers; | ||
new: FactoryFunction; | ||
}; | ||
/** | ||
* Returns a new log function for the given logger `name` | ||
*/ | ||
export type FactoryFunction = (name: string) => LogFunction; | ||
/** | ||
* Has a method `ext` which is an extension | ||
*/ | ||
export type HasExtension = { | ||
/** | ||
* Called when a log function needs to be extended, either because it was | ||
* newly created, or because it's configuration or settings changed. | ||
* | ||
* This function implements `enabledFor` and a log method for | ||
* each level in `anylogger.levels` on the given `logfn`. | ||
* | ||
* This function can safely be called multiple times on the same `logfn`. | ||
* | ||
* The default extension provided here is essentially a no-op extension. | ||
* | ||
* Adapters for common logging frameworks such as the | ||
* [console](https://npmjs.com/package/anylogger-console), | ||
* [debug](https://npmjs.com/package/anylogger-debug), | ||
* [loglevel](https://npmjs.com/package/anylogger-loglevel), | ||
* [ulog](https://npmjs.com/package/ulog) and | ||
* [log4js](https://npmjs.com/package/log4js) override | ||
* this default extension. | ||
* | ||
* @param logfn The log function to be extended | ||
* | ||
* @return The log function that was given, extended to a Logger | ||
*/ | ||
ext: Extension; | ||
}; | ||
/** | ||
* An extension accepts a LogFunction and returns a Logger | ||
*/ | ||
export type Extension = (logfn: LogFunction) => Logger; | ||
/** | ||
* An Adapter accepts the AnyLogger function and adapts it | ||
* by overriding the default extension | ||
*/ | ||
export type Adapter = (anylogger: AnyLogger) => void; | ||
/** | ||
* Anylogger supports the concept of levels. | ||
*/ | ||
export type HasLevels = { | ||
/** | ||
* An object containing a mapping of level names to level values. | ||
@@ -42,18 +146,31 @@ * | ||
*/ | ||
levels: { | ||
[level: string]: number; | ||
}; | ||
levels: LogLevels; | ||
}; | ||
/** | ||
* A log level is a string that is a key of `LogLevels` | ||
*/ | ||
export type LogLevel = keyof LogLevels; | ||
/** | ||
* A mapping of level name `string` to level `number` that consists | ||
* at least the keys from `logLevels` | ||
*/ | ||
export type LogLevels = typeof logLevels & { | ||
[level: string]: number; | ||
}; | ||
/** | ||
* A default set of level name/value pairs that maps well to the console. | ||
*/ | ||
declare const logLevels: { | ||
error: number; | ||
warn: number; | ||
info: number; | ||
log: number; | ||
debug: number; | ||
trace: number; | ||
}; | ||
/** | ||
* You can call any logger via anylogger | ||
*/ | ||
export type HasDelegate = { | ||
/** | ||
* Called when a new logger needs to be created. | ||
* | ||
* The default implementation creates a log function that | ||
* allows for an optional first argument, preceding the | ||
* message argument(s), to specify the level to call. | ||
* | ||
* | ||
* @param name The name for the new logger | ||
* @returns The newly created logger | ||
*/ | ||
new: (name: LoggerName) => LogFunction; | ||
/** | ||
* Called by the log function that the default implementation of | ||
@@ -72,7 +189,13 @@ * `anylogger.new` creates. | ||
* ```ts | ||
* log('message') // calls log.log('message') | ||
* log('error') // calls log.log('error') | ||
* log('info', 'message') // calls log.info('message') | ||
* log('error', 'message') // calls log.error('message') | ||
* log('Hello', 'World!') // calls log.log('Hello', 'World!') | ||
* import anylogger from 'anylogger' | ||
* // calls anylogger('my-log').log('message') | ||
* anylogger.log('my-log', 'message') | ||
* // calls anylogger('my-log').log('error') | ||
* anylogger.log('my-log', 'error') | ||
* // calls anylogger('my-log').info('message') | ||
* anylogger.log('my-log', 'info', 'message') | ||
* // calls anylogger('my-log').error('message') | ||
* anylogger.log('my-log', 'error', 'message') | ||
* // calls anylogger('my-log').log('Hello', 'World!') | ||
* anylogger.log('my-log', 'Hello', 'World!') | ||
* ``` | ||
@@ -87,72 +210,13 @@ * | ||
*/ | ||
log: (name: LoggerName, ...args: any) => void; | ||
/** | ||
* Called when a log function needs to be extended, either because it was | ||
* newly created, or because it's configuration or settings changed. | ||
* | ||
* This function implements `enabledFor` and a log method for | ||
* each level in `anylogger.levels` on the given `logfn`. | ||
* | ||
* This function can safely be called multiple times on the same `logfn`. | ||
* | ||
* The default adapter provided here is essentially a no-op adapter. | ||
* Adapters for common logging frameworks such as the | ||
* [console](https://npmjs.com/package/anylogger-console), | ||
* [debug](https://npmjs.com/package/anylogger-debug), | ||
* [loglevel](https://npmjs.com/package/anylogger-loglevel), | ||
* [ulog](https://npmjs.com/package/ulog) and | ||
* [log4js](https://npmjs.com/package/log4js) override | ||
* this default adapter. | ||
* | ||
* @param logfn The log function to be extended | ||
* | ||
* @return The log function that was given, extended to a Logger | ||
*/ | ||
ext: Adapter; | ||
log: Delegate; | ||
}; | ||
/** | ||
* A log function is a function that takes a variable amount of | ||
* arguments and returns void. | ||
* Method that takes a logger name and arguments and calls the right log method | ||
* based on that information. | ||
* | ||
* This function inspects the given `args` to identify the log level and then | ||
* calls the log method corresponding to that level on the logger with `name`. | ||
*/ | ||
export type LogFunction = (...args: any) => void; | ||
/** | ||
* An adapter accepts a LogFunction and returns a Logger | ||
*/ | ||
export type Adapter = (logfn: LogFunction) => Logger; | ||
/** | ||
* A logger is a log function that has a `name` that corresponds to the logger | ||
* name, a method `enabledFor(level: LogLevel)` to check whether the logger is | ||
* enabled for a certain log level, and log methods for each of the log levels | ||
* supported by AnyLogger: `error`, `warn`, `info`, `log`, `debug` and `trace`. | ||
*/ | ||
export type Logger = LogFunction & { | ||
readonly name: LoggerName; | ||
enabledFor: (level?: LogLevel) => boolean | void; | ||
} & { | ||
[P in keyof LogLevels as `${P}`]: LogFunction; | ||
}; | ||
export type LogLevels = { | ||
error: 1; | ||
warn: 2; | ||
info: 3; | ||
log: 4; | ||
debug: 5; | ||
trace: 6; | ||
}; | ||
/** | ||
* A log level is a string that is a key of `LogLevels` | ||
*/ | ||
export type LogLevel = keyof LogLevels; | ||
/** | ||
* All loggers, keyed by name | ||
*/ | ||
export type AllLoggers = { | ||
[name: string]: Logger; | ||
}; | ||
/** | ||
* An alias for the much used concept of a LoggerName | ||
*/ | ||
export type LoggerName = string; | ||
declare const anylogger: AnyLogger; | ||
export type Delegate = (name: string, ...args: any[]) => void; | ||
export default anylogger; | ||
//# sourceMappingURL=anylogger.d.ts.map |
@@ -17,25 +17,13 @@ var anylogger = (function () { | ||
(anylogger.all[name] = anylogger.ext(anylogger.new(name)))); | ||
// all loggers created so far | ||
// anylogger.all stores all loggers created so far | ||
anylogger.all = Object.create(null); | ||
// the supported levels | ||
anylogger.levels = { error: 1, warn: 2, info: 3, log: 4, debug: 5, trace: 6 }; | ||
// creates a new named log function | ||
anylogger.new = (name) => ({ | ||
// to assign the function `name`, set it to a named key in an object. | ||
// the default implementation calls `anylogger.log`, which should be a | ||
// good choice in many cases. | ||
[name]: (...args) => anylogger.log(name, ...args) | ||
}[name]); // return only the function, not the encapsulating object | ||
// logs with the logger with the given `name` | ||
anylogger.log = (name, ...args) => { | ||
// select the logger to use | ||
anylogger.all[name][ | ||
// select the level to use | ||
// if multiple args and first matches a level name | ||
(((args.length > 1) && anylogger.levels[args[0]]) | ||
? args.shift() // use the level from the args | ||
: 'log' // else use default level `'log'` | ||
)](...args); // call method matching level with remaining args | ||
}; | ||
// extends the given `logger` function | ||
// anylogger.new creates a new named log function | ||
anylogger.new = (name) => ( | ||
// to assign the function `name`, set it to a named key in an object. | ||
// the default implementation calls `anylogger.log`, which should be a | ||
// good choice in most cases. | ||
{ [name]: (...args) => anylogger.log(name, ...args) } | ||
// return only the function, not the encapsulating object | ||
[name]); | ||
// anylogger.ext extends the given `logger` function | ||
// the implementation here only adds no-ops | ||
@@ -45,7 +33,24 @@ // adapters should change this behavior | ||
logger.enabledFor = () => { }; | ||
for (const method in anylogger.levels) { | ||
logger[method] = () => { }; | ||
for (const lvl in anylogger.levels) { | ||
logger[lvl] = () => { }; | ||
} | ||
return logger; | ||
}; | ||
/** | ||
* A default set of level name/value pairs that maps well to the console. | ||
*/ | ||
const logLevels = { error: 1, warn: 2, info: 3, log: 4, debug: 5, trace: 6 }; | ||
// the minimal supported levels | ||
anylogger.levels = logLevels; | ||
// logs with the logger with the given `name` | ||
anylogger.log = (name, ...args) => ( | ||
// select the logger to use | ||
anylogger(name)[ | ||
// select the level to use | ||
// if multiple args and first matches a level name | ||
(((args.length > 1) && anylogger.levels[args[0]]) | ||
? args.shift() // use the level from the args | ||
: 'log' // else use default level `'log'` | ||
)](...args) // call method matching level with remaining args | ||
); | ||
@@ -52,0 +57,0 @@ return anylogger; |
@@ -15,25 +15,13 @@ /** | ||
(anylogger.all[name] = anylogger.ext(anylogger.new(name)))); | ||
// all loggers created so far | ||
// anylogger.all stores all loggers created so far | ||
anylogger.all = Object.create(null); | ||
// the supported levels | ||
anylogger.levels = { error: 1, warn: 2, info: 3, log: 4, debug: 5, trace: 6 }; | ||
// creates a new named log function | ||
anylogger.new = (name) => ({ | ||
// to assign the function `name`, set it to a named key in an object. | ||
// the default implementation calls `anylogger.log`, which should be a | ||
// good choice in many cases. | ||
[name]: (...args) => anylogger.log(name, ...args) | ||
}[name]); // return only the function, not the encapsulating object | ||
// logs with the logger with the given `name` | ||
anylogger.log = (name, ...args) => { | ||
// select the logger to use | ||
anylogger.all[name][ | ||
// select the level to use | ||
// if multiple args and first matches a level name | ||
(((args.length > 1) && anylogger.levels[args[0]]) | ||
? args.shift() // use the level from the args | ||
: 'log' // else use default level `'log'` | ||
)](...args); // call method matching level with remaining args | ||
}; | ||
// extends the given `logger` function | ||
// anylogger.new creates a new named log function | ||
anylogger.new = (name) => ( | ||
// to assign the function `name`, set it to a named key in an object. | ||
// the default implementation calls `anylogger.log`, which should be a | ||
// good choice in most cases. | ||
{ [name]: (...args) => anylogger.log(name, ...args) } | ||
// return only the function, not the encapsulating object | ||
[name]); | ||
// anylogger.ext extends the given `logger` function | ||
// the implementation here only adds no-ops | ||
@@ -43,10 +31,27 @@ // adapters should change this behavior | ||
logger.enabledFor = () => { }; | ||
for (const method in anylogger.levels) { | ||
logger[method] = () => { }; | ||
for (const lvl in anylogger.levels) { | ||
logger[lvl] = () => { }; | ||
} | ||
return logger; | ||
}; | ||
// this is a real ESM module | ||
/** | ||
* A default set of level name/value pairs that maps well to the console. | ||
*/ | ||
const logLevels = { error: 1, warn: 2, info: 3, log: 4, debug: 5, trace: 6 }; | ||
// the minimal supported levels | ||
anylogger.levels = logLevels; | ||
// logs with the logger with the given `name` | ||
anylogger.log = (name, ...args) => ( | ||
// select the logger to use | ||
anylogger(name)[ | ||
// select the level to use | ||
// if multiple args and first matches a level name | ||
(((args.length > 1) && anylogger.levels[args[0]]) | ||
? args.shift() // use the level from the args | ||
: 'log' // else use default level `'log'` | ||
)](...args) // call method matching level with remaining args | ||
); | ||
// this is an esm module | ||
// we transpile the compiled Javascript back to commonjs with rollup | ||
export default anylogger; | ||
//# sourceMappingURL=anylogger.js.map |
@@ -1,1 +0,1 @@ | ||
var anylogger=function(){const n=l=>n.all[l]||(n.all[l]=n.ext(n.new(l)));return n.all=Object.create(null),n.levels={error:1,warn:2,info:3,log:4,debug:5,trace:6},n.new=e=>({[e]:(...l)=>n.log(e,...l)})[e],n.log=(l,...e)=>{n.all[l][1<e.length&&n.levels[e[0]]?e.shift():"log"](...e)},n.ext=l=>{l.enabledFor=()=>{};for(const e in n.levels)l[e]=()=>{};return l},n}(); | ||
var anylogger=function(){const n=e=>n.all[e]||(n.all[e]=n.ext(n.new(e)));n.all=Object.create(null),n.new=l=>({[l]:(...e)=>n.log(l,...e)})[l],n.ext=e=>{e.enabledFor=()=>{};for(const l in n.levels)e[l]=()=>{};return e};return n.levels={error:1,warn:2,info:3,log:4,debug:5,trace:6},n.log=(e,...l)=>n(e)[1<l.length&&n.levels[l[0]]?l.shift():"log"](...l),n}(); |
316
anylogger.ts
@@ -11,37 +11,74 @@ /** | ||
/** | ||
* Gets or creates a logger by name | ||
* Gets or creates a logger by name. | ||
* | ||
* Acts as a store for all loggers created so far, | ||
* as a factory to create new loggers and as an | ||
* adapter (the no-op adapter) to extend the loggers. | ||
* | ||
* Anylogger has the concept of levels and it can be | ||
* used as a delegate to log with a certain logger. | ||
*/ | ||
export type AnyLogger = ((name: LoggerName) => Logger) & { | ||
export type AnyLogger = GetLogger | ||
& HasStore & HasFactory & HasExtension | ||
& HasLevels & HasDelegate | ||
/** | ||
* Stores all loggers created so far | ||
*/ | ||
all: AllLoggers; | ||
/** | ||
* Gets a logger by `name`. | ||
*/ | ||
export type GetLogger = (name: string) => Logger | ||
/** | ||
* An object containing a mapping of level names to level values. | ||
* | ||
* To be compliant with the anylogger API, loggers should support at least | ||
* the log methods corresponding to the default levels specified here, but | ||
* they may define additional levels and they may choose to use different | ||
* numeric values for all the levels. | ||
* | ||
* The guarantees the Anylogger API makes are: | ||
* - there is a log method for each level listed in anylogger.levels | ||
* - the levels error, warn, info, log, debug and trace are always there | ||
* - each level corresponds to a numeric value | ||
* | ||
* Note that the Anylogger API explicitly does not guarantee that all levels | ||
* have distinct values or that the numeric values will follow any pattern | ||
* or have any specific order. For this reason it is best to think of levels | ||
* as separate log channels, possibly going to different output locations, | ||
* instead of thinking of them as having a specific order. | ||
* | ||
* Adapters can modify this object to include levels corresponding with | ||
* those available in the logging library the adapter is for. Adapters will | ||
* ensure to always include the default levels and to have a log method for | ||
* each level, so all code can rely on that contract. | ||
*/ | ||
levels: { [level: string]: number; }; | ||
/** | ||
* A logger is a log function that has a `name` that corresponds to the logger | ||
* name, a method `enabledFor(level: LogLevel)` to check whether the logger is | ||
* enabled for a certain log level, and log methods for each of the log levels | ||
* supported by AnyLogger: `error`, `warn`, `info`, `log`, `debug` and `trace`. | ||
*/ | ||
export type Logger = LogFunction & LogObject & { | ||
readonly name: string | ||
enabledFor: (level?: LogLevel) => boolean | void | ||
} | ||
/** | ||
* A log function is a function that takes a variable amount of | ||
* arguments and returns void. | ||
*/ | ||
export type LogFunction = (...args: any) => void | ||
/** | ||
* A log object is an object that has methods corresponding to all | ||
* supported log levels, where each method is a log function. | ||
*/ | ||
export type LogObject = { | ||
[P in keyof LogLevels as `${P}`]: LogFunction | ||
} | ||
// the main `anylogger` function | ||
const anylogger: AnyLogger = (name) => ( | ||
// return the existing logger, or | ||
anylogger.all[name] || | ||
// create and store a new logger with that name | ||
(anylogger.all[name] = anylogger.ext(anylogger.new(name))) | ||
) | ||
/** | ||
* Stores all loggers created so far | ||
*/ | ||
export type HasStore = { | ||
all: AllLoggers | ||
} | ||
/** | ||
* All loggers, keyed by name | ||
*/ | ||
export type AllLoggers = { | ||
[name: string]: Logger | ||
} | ||
// anylogger.all stores all loggers created so far | ||
anylogger.all = Object.create(null) as AllLoggers | ||
/** | ||
* Anylogger creates new loggers when needed | ||
*/ | ||
export type HasFactory = { | ||
/** | ||
@@ -54,38 +91,27 @@ * Called when a new logger needs to be created. | ||
* | ||
* | ||
* @param name The name for the new logger | ||
* @returns The newly created logger | ||
*/ | ||
new: (name: LoggerName) => LogFunction | ||
new: FactoryFunction | ||
} | ||
/** | ||
* Called by the log function that the default implementation of | ||
* `anylogger.new` creates. | ||
* | ||
* This log function calls the right log method on the right logger, | ||
* based on the `name` of the logger and the arguments given in `args`. | ||
* | ||
* if there is more than one argument given and the first argument is a | ||
* string that corresponds to a log level, that level will be called. | ||
* Otherwise it defaults to calling the `log` method. | ||
* | ||
* E.g. | ||
* | ||
* ```ts | ||
* log('message') // calls log.log('message') | ||
* log('error') // calls log.log('error') | ||
* log('info', 'message') // calls log.info('message') | ||
* log('error', 'message') // calls log.error('message') | ||
* log('Hello', 'World!') // calls log.log('Hello', 'World!') | ||
* ``` | ||
* | ||
* Having this code in anylogger makes writing adapters easier, because | ||
* they only have to override `anylogger.ext` and add the logging methods | ||
* to the new logger. | ||
* | ||
* @param name The name of the logger | ||
* @param args The arguments for the logger | ||
*/ | ||
log: (name: LoggerName, ...args: any) => void | ||
/** | ||
* Returns a new log function for the given logger `name` | ||
*/ | ||
export type FactoryFunction = (name: string) => LogFunction | ||
// anylogger.new creates a new named log function | ||
anylogger.new = (name) => ( | ||
// to assign the function `name`, set it to a named key in an object. | ||
// the default implementation calls `anylogger.log`, which should be a | ||
// good choice in most cases. | ||
{ [name]: (...args: any[]) => anylogger.log(name, ...args) } | ||
// return only the function, not the encapsulating object | ||
[name] | ||
) | ||
/** | ||
* Has a method `ext` which is an extension | ||
*/ | ||
export type HasExtension = { | ||
/** | ||
@@ -100,3 +126,4 @@ * Called when a log function needs to be extended, either because it was | ||
* | ||
* The default adapter provided here is essentially a no-op adapter. | ||
* The default extension provided here is essentially a no-op extension. | ||
* | ||
* Adapters for common logging frameworks such as the | ||
@@ -108,3 +135,3 @@ * [console](https://npmjs.com/package/anylogger-console), | ||
* [log4js](https://npmjs.com/package/log4js) override | ||
* this default adapter. | ||
* this default extension. | ||
* | ||
@@ -115,31 +142,59 @@ * @param logfn The log function to be extended | ||
*/ | ||
ext: Adapter | ||
ext: Extension | ||
} | ||
/** | ||
* A log function is a function that takes a variable amount of | ||
* arguments and returns void. | ||
* An extension accepts a LogFunction and returns a Logger | ||
*/ | ||
export type LogFunction = (...args: any) => void | ||
export type Extension = (logfn: LogFunction) => Logger | ||
/** | ||
* An adapter accepts a LogFunction and returns a Logger | ||
* An Adapter accepts the AnyLogger function and adapts it | ||
* by overriding the default extension | ||
*/ | ||
export type Adapter = (logfn: LogFunction) => Logger | ||
export type Adapter = (anylogger: AnyLogger) => void | ||
// anylogger.ext extends the given `logger` function | ||
// the implementation here only adds no-ops | ||
// adapters should change this behavior | ||
anylogger.ext = (logger: LogFunction): Logger => { | ||
(logger as Logger).enabledFor = ()=>{} | ||
for (const lvl in anylogger.levels) { | ||
(logger as Logger)[lvl as LogLevel] = ()=>{} | ||
} | ||
return logger as Logger | ||
} | ||
/** | ||
* A logger is a log function that has a `name` that corresponds to the logger | ||
* name, a method `enabledFor(level: LogLevel)` to check whether the logger is | ||
* enabled for a certain log level, and log methods for each of the log levels | ||
* supported by AnyLogger: `error`, `warn`, `info`, `log`, `debug` and `trace`. | ||
* Anylogger supports the concept of levels. | ||
*/ | ||
export type Logger = LogFunction & { | ||
readonly name: LoggerName; | ||
enabledFor: (level?: LogLevel) => boolean | void; | ||
} & { | ||
[P in keyof LogLevels as `${P}`]: LogFunction; | ||
export type HasLevels = { | ||
/** | ||
* An object containing a mapping of level names to level values. | ||
* | ||
* To be compliant with the anylogger API, loggers should support at least | ||
* the log methods corresponding to the default levels specified here, but | ||
* they may define additional levels and they may choose to use different | ||
* numeric values for all the levels. | ||
* | ||
* The guarantees the Anylogger API makes are: | ||
* - there is a log method for each level listed in anylogger.levels | ||
* - the levels error, warn, info, log, debug and trace are always there | ||
* - each level corresponds to a numeric value | ||
* | ||
* Note that the Anylogger API explicitly does not guarantee that all levels | ||
* have distinct values or that the numeric values will follow any pattern | ||
* or have any specific order. For this reason it is best to think of levels | ||
* as separate log channels, possibly going to different output locations, | ||
* instead of thinking of them as having a specific order. | ||
* | ||
* Adapters can modify this object to include levels corresponding with | ||
* those available in the logging library the adapter is for. Adapters will | ||
* ensure to always include the default levels and to have a log method for | ||
* each level, so all code can rely on that contract. | ||
*/ | ||
levels: LogLevels; | ||
} | ||
export type LogLevels = { error:1, warn:2, info:3, log:4, debug:5, trace:6 } | ||
/** | ||
@@ -151,39 +206,69 @@ * A log level is a string that is a key of `LogLevels` | ||
/** | ||
* All loggers, keyed by name | ||
* A mapping of level name `string` to level `number` that consists | ||
* at least the keys from `logLevels` | ||
*/ | ||
export type AllLoggers = { | ||
[name: string]: Logger | ||
} | ||
export type LogLevels = typeof logLevels & { [level: string]: number } | ||
/** | ||
* An alias for the much used concept of a LoggerName | ||
* A default set of level name/value pairs that maps well to the console. | ||
*/ | ||
export type LoggerName = string | ||
const logLevels = { error: 1, warn: 2, info: 3, log: 4, debug: 5, trace: 6 } | ||
// the main `anylogger` function | ||
const anylogger: AnyLogger = (name) => ( | ||
// return the existing logger, or | ||
anylogger.all[name] || | ||
// create and store a new logger with that name | ||
(anylogger.all[name] = anylogger.ext(anylogger.new(name))) | ||
) | ||
// the minimal supported levels | ||
anylogger.levels = logLevels | ||
// all loggers created so far | ||
anylogger.all = Object.create(null) as AllLoggers; | ||
/** | ||
* You can call any logger via anylogger | ||
*/ | ||
export type HasDelegate = { | ||
/** | ||
* Called by the log function that the default implementation of | ||
* `anylogger.new` creates. | ||
* | ||
* This log function calls the right log method on the right logger, | ||
* based on the `name` of the logger and the arguments given in `args`. | ||
* | ||
* if there is more than one argument given and the first argument is a | ||
* string that corresponds to a log level, that level will be called. | ||
* Otherwise it defaults to calling the `log` method. | ||
* | ||
* E.g. | ||
* | ||
* ```ts | ||
* import anylogger from 'anylogger' | ||
* // calls anylogger('my-log').log('message') | ||
* anylogger.log('my-log', 'message') | ||
* // calls anylogger('my-log').log('error') | ||
* anylogger.log('my-log', 'error') | ||
* // calls anylogger('my-log').info('message') | ||
* anylogger.log('my-log', 'info', 'message') | ||
* // calls anylogger('my-log').error('message') | ||
* anylogger.log('my-log', 'error', 'message') | ||
* // calls anylogger('my-log').log('Hello', 'World!') | ||
* anylogger.log('my-log', 'Hello', 'World!') | ||
* ``` | ||
* | ||
* Having this code in anylogger makes writing adapters easier, because | ||
* they only have to override `anylogger.ext` and add the logging methods | ||
* to the new logger. | ||
* | ||
* @param name The name of the logger | ||
* @param args The arguments for the logger | ||
*/ | ||
log: Delegate | ||
} | ||
// the supported levels | ||
anylogger.levels = { error: 1, warn: 2, info: 3, log: 4, debug: 5, trace: 6 } | ||
/** | ||
* Method that takes a logger name and arguments and calls the right log method | ||
* based on that information. | ||
* | ||
* This function inspects the given `args` to identify the log level and then | ||
* calls the log method corresponding to that level on the logger with `name`. | ||
*/ | ||
export type Delegate = (name: string, ...args: any[]) => void | ||
// creates a new named log function | ||
anylogger.new = (name: LoggerName): LogFunction => ({ | ||
// to assign the function `name`, set it to a named key in an object. | ||
// the default implementation calls `anylogger.log`, which should be a | ||
// good choice in many cases. | ||
[name]: (...args: any) => anylogger.log(name, ...args) | ||
}[name]) // return only the function, not the encapsulating object | ||
// logs with the logger with the given `name` | ||
anylogger.log = (name: LoggerName, ...args: any) => { | ||
anylogger.log = (name, ...args) => ( | ||
// select the logger to use | ||
anylogger.all[name][ | ||
anylogger(name)[ | ||
// select the level to use | ||
@@ -196,17 +281,6 @@ // if multiple args and first matches a level name | ||
](...args) // call method matching level with remaining args | ||
} | ||
) | ||
// extends the given `logger` function | ||
// the implementation here only adds no-ops | ||
// adapters should change this behavior | ||
anylogger.ext = (logger: LogFunction): Logger => { | ||
(logger as Logger).enabledFor = ()=>{} | ||
for (const method in anylogger.levels) { | ||
(logger as Logger)[method as LogLevel] = ()=>{} | ||
} | ||
return logger as Logger | ||
} | ||
// this is a real ESM module | ||
// this is an esm module | ||
// we transpile the compiled Javascript back to commonjs with rollup | ||
export default anylogger |
{ | ||
"name": "anylogger", | ||
"version": "1.1.0-beta.0", | ||
"version": "1.1.0-beta.1", | ||
"description": "Get a logger. Any logger.", | ||
"type": "module", | ||
"description": "Get a logger. Any logger.", | ||
"exports": { | ||
".": { | ||
"import": "./anylogger.js", | ||
"require": "./anylogger.cjs" | ||
} | ||
}, | ||
"src": "./anylogger.js", | ||
@@ -14,8 +20,2 @@ "main": "./anylogger.js", | ||
"unpkg": "./anylogger.min.js", | ||
"exports": { | ||
".": { | ||
"import": "./anylogger.js", | ||
"require": "./anylogger.cjs" | ||
} | ||
}, | ||
"files": [ | ||
@@ -22,0 +22,0 @@ "anylogger.cjs", |
314
README.md
@@ -1,2 +0,2 @@ | ||
# anylogger <sub><sup>1.0.11</sup></sub> | ||
# anylogger <sub><sup>1.1.0-beta.1</sup></sub> | ||
### Get a logger. Any logger. | ||
@@ -33,3 +33,3 @@ | ||
<td><h5>Install</h5> | ||
<pre>npm i -P anylogger</pre> | ||
<pre>npm i -D anylogger</pre> | ||
<h5>Use</h5> | ||
@@ -39,4 +39,5 @@ <pre>import anylogger from 'anylogger' | ||
log('Anylogger is easy!')</pre> | ||
<p>Install your preferred logger | ||
and it's adapter as <a href="#install-dev-dependencies-in-a-library-project">dev dependencies</a>.</p> | ||
<p>Add to peerDependencies and install | ||
an adapter as <a href="#install-dev-dependencies-in-a-library-project" | ||
>dev dependencies</a>.</p> | ||
</td> | ||
@@ -48,3 +49,5 @@ <td><h5>Install</h5> | ||
<i>index.js</i> | ||
<pre>import "anylogger-debug"</pre> | ||
<pre>import adapter from "anylogger-debug" | ||
import anylogger from 'anylogger' | ||
adapter(anylogger)</pre> | ||
<h5>Use</h5> | ||
@@ -60,3 +63,5 @@ <pre>import anylogger from 'anylogger' | ||
<i>index.js</i> | ||
<pre>import "anylogger-loglevel"</pre> | ||
<pre>import adapter from "anylogger-loglevel" | ||
import anylogger from 'anylogger' | ||
adapter(anylogger)</pre> | ||
<h5>Use</h5> | ||
@@ -72,3 +77,5 @@ <pre>import anylogger from 'anylogger' | ||
<i>index.js</i> | ||
<pre>import "ulog"</pre> | ||
<pre>import adapter from "ulog" | ||
import anylogger from 'anylogger' | ||
adapter(anylogger)</pre> | ||
<h5>Use</h5> | ||
@@ -84,3 +91,5 @@ <pre>import anylogger from 'anylogger' | ||
<i>index.js</i> | ||
<pre>import "anylogger-log4js"</pre> | ||
<pre>import adapter from "anylogger-log4js" | ||
import anylogger from 'anylogger' | ||
adapter(anylogger)</pre> | ||
<h5>Use</h5> | ||
@@ -116,3 +125,3 @@ <pre>import anylogger from 'anylogger' | ||
A tiny ~[264](#gzip-size) bytes logging facade that you can include in your | ||
A tiny ~[259](#gzip-size) bytes logging facade that you can include in your | ||
library to support logging, while at the same time allowing application | ||
@@ -124,3 +133,3 @@ developers to plug in any logging framework they choose. | ||
or just abandoning logging altogether, choose `anylogger` and for just | ||
~[264](#gzip-size) bytes shared between all libraries doing this, we can | ||
~[259](#gzip-size) bytes shared between all libraries doing this, we can | ||
plug in any framework of our choice and all libraries will automatically | ||
@@ -132,12 +141,12 @@ start to use that framework. Wouldn't it be much better and easier? | ||
* [anylogger.ts](https://unpkg.com/anylogger@1.1.0-beta.0/anylogger.ts) | ||
(fully commented source ~6kB) | ||
* [anylogger.d.ts](https://unpkg.com/anylogger@1.1.0-beta.0/anylogger.d.ts) | ||
(typescript type definitions ~5kB) | ||
* [anylogger.js](https://unpkg.com/anylogger@1.1.0-beta.0/anylogger.js) | ||
(transpiled es6 ecmascript module ~1kB) | ||
* [anylogger.cjs](https://unpkg.com/anylogger@1.1.0-beta.0/anylogger.cjs) | ||
(transpiled es6 commonjs module ~1kB) | ||
* [anylogger.min.js](https://unpkg.com/anylogger@1.1.0-beta.0/anylogger.min.js) | ||
(minified 361 bytes, gzipped ~[264](#gzip-size) bytes) | ||
* [anylogger.ts](https://unpkg.com/anylogger@1.1.0-beta.1/anylogger.ts) | ||
(fully commented source ~7kB) | ||
* [anylogger.d.ts](https://unpkg.com/anylogger@1.1.0-beta.1/anylogger.d.ts) | ||
(typescript type definitions ~6kB) | ||
* [anylogger.js](https://unpkg.com/anylogger@1.1.0-beta.1/anylogger.js) | ||
(javascript esm module ~2kB) | ||
* [anylogger.cjs](https://unpkg.com/anylogger@1.1.0-beta.1/anylogger.cjs) | ||
(javascript commonjs module ~2kB) | ||
* [anylogger.min.js](https://unpkg.com/anylogger@1.1.0-beta.1/anylogger.min.js) | ||
(minified 355 bytes, gzipped ~[259](#gzip-size) bytes) | ||
@@ -149,3 +158,3 @@ | ||
```html | ||
<script src="https://unpkg.com/anylogger@1.1.0-beta.0"></script> | ||
<script src="https://unpkg.com/anylogger@1.1.0-beta.1"></script> | ||
<script>(function(){ // IIFE | ||
@@ -164,25 +173,15 @@ var log = anylogger('index.html') | ||
Depending on your project type, install anylogger, your logging framework of | ||
choice and an anylogger adapter if needed. | ||
Always only install anylogger in application projects. In library projects, | ||
install anylogger as a dev dependency and make it a peer dependency. | ||
### Install in a library project | ||
### Install in an application project | ||
If you are building a library, install anylogger as a dependency: | ||
If you are building an application project and have selected a logging | ||
framework, install anylogger, the selected logging framework and the | ||
adapter for that logging framework if needed. | ||
```sh | ||
npm install --save anylogger | ||
``` | ||
This will add `anylogger` as a dependency to your `package.json`. | ||
### Install dev dependencies in a library project | ||
If you are building a library, you can use the logging framework | ||
you prefer without tightly coupling your library to it by installing | ||
that library and the adapter for it if needed as development dependencies: | ||
**For [anylogger-console](https://npmjs.com/package/anylogger-console)**: | ||
```sh | ||
npm install --save-dev anylogger-console | ||
npm install --save anylogger anylogger-console | ||
``` | ||
@@ -193,3 +192,3 @@ | ||
```sh | ||
npm install --save-dev debug anylogger-debug | ||
npm install --save anylogger debug anylogger-debug | ||
``` | ||
@@ -201,10 +200,10 @@ > See [anylogger-debug](https://npmjs.com/package/anylogger-debug) | ||
```sh | ||
npm install --save-dev loglevel anylogger-loglevel | ||
npm install --save anylogger loglevel anylogger-loglevel | ||
``` | ||
> See [anylogger-loglevel](https://npmjs.com/package/anylogger-loglevel) | ||
**For [ulog](https://npmjs.com/package/ulog)**: | ||
**For [ulog](https://npmjs.com/package/ulog)** | ||
```sh | ||
npm install --save-dev ulog | ||
npm install --save anylogger ulog | ||
``` | ||
@@ -216,16 +215,56 @@ > No adapter is needed for `ulog` | ||
```sh | ||
npm install --save-dev log4js anylogger-log4js | ||
npm install --save anylogger log4js anylogger-log4js | ||
``` | ||
> See [anylogger-log4js](https://npmjs.com/package/anylogger-log4js) | ||
### Install in an application project | ||
Check out all | ||
[available adapters](https://www.npmjs.com/search?q=keywords:anylogger). | ||
If you are building an application project and have selected a logging | ||
framework, install anylogger, the selected logging framework and the | ||
adapter for that logging framework if needed. | ||
### Install in a library project | ||
If you are building a library, install anylogger as a dev-dependency: | ||
```sh | ||
npm install --save-dev anylogger | ||
``` | ||
This will add `anylogger` as a dev-dependency to your `package.json`. | ||
To ensure that application projects using your library also install | ||
`anylogger`, make it a peer dependency. Add this to *package.json*: | ||
```json | ||
"peerDependencies": { | ||
"anylogger": "1.x || >=1.1.0-beta || >=1.2.0-beta || >=1.3.0-beta || >=1.4.0-beta || >=1.5.0-beta || >=1.6.0-beta || >=1.7.0-beta || >=1.8.0-beta || >=1.9.0-beta" | ||
} | ||
``` | ||
> | ||
> The list of (future) betas is an annoyance in the way NPM deals with beta | ||
> tags unfortunately. IMHO it is clear that `1.1.0-beta` matches `1.x` and | ||
> should thus be covered by it, but alas, NPM thinks otherwise. | ||
> | ||
> If you want your library to also work with possible future betas of | ||
> anylogger, then include this list of betas (recommended). | ||
> | ||
Add anylogger to your install instructions in your project README.md: | ||
> | ||
> ### Install | ||
> | ||
> `npm install --save my-library anylogger` | ||
> | ||
### Install dev dependencies in a library project | ||
If you are building a library, you can use the logging framework | ||
you prefer without tightly coupling your library to it by installing | ||
that library and the adapter for it if needed as development dependencies: | ||
**For [anylogger-console](https://npmjs.com/package/anylogger-console)**: | ||
```sh | ||
npm install --save anylogger anylogger-console | ||
npm install --save-dev anylogger-console | ||
``` | ||
@@ -236,3 +275,3 @@ | ||
```sh | ||
npm install --save anylogger debug anylogger-debug | ||
npm install --save-dev debug anylogger-debug | ||
``` | ||
@@ -244,10 +283,10 @@ > See [anylogger-debug](https://npmjs.com/package/anylogger-debug) | ||
```sh | ||
npm install --save anylogger loglevel anylogger-loglevel | ||
npm install --save-dev loglevel anylogger-loglevel | ||
``` | ||
> See [anylogger-loglevel](https://npmjs.com/package/anylogger-loglevel) | ||
**For [ulog](https://npmjs.com/package/ulog)** | ||
**For [ulog](https://npmjs.com/package/ulog)**: | ||
```sh | ||
npm install --save anylogger ulog | ||
npm install --save-dev ulog | ||
``` | ||
@@ -259,30 +298,31 @@ > No adapter is needed for `ulog` | ||
```sh | ||
npm install --save anylogger log4js anylogger-log4js | ||
npm install --save-dev log4js anylogger-log4js | ||
``` | ||
> See [anylogger-log4js](https://npmjs.com/package/anylogger-log4js) | ||
Check out all | ||
[available adapters](https://www.npmjs.com/search?q=keywords:anylogger). | ||
## Use | ||
## Include | ||
Depending on the type of project, either just use anylogger, | ||
or also include the adapter. | ||
Depending on the type of project, either just use anylogger, or also include the adapter. | ||
### Use in a library | ||
### Include in a library | ||
In your library code, only use anylogger and restrict yourself to the | ||
[Anylogger API](#anylogger-api) to stay framework-independent: | ||
#### require | ||
#### import | ||
*my-library.js* | ||
```js | ||
var log = require('anylogger')('my-library') | ||
import anylogger from 'anylogger' | ||
const log = anylogger('my-library') | ||
log.info('Logging is easy!') | ||
``` | ||
#### import | ||
#### require | ||
*my-library.js* | ||
```js | ||
import anylogger from 'anylogger' | ||
const anylogger = require('anylogger') | ||
const log = anylogger('my-library') | ||
log.info('Logging is easy!') | ||
``` | ||
@@ -292,67 +332,86 @@ | ||
### Include in tests for your library | ||
### Use in tests for your library | ||
In the tests for your library code, you can include an adapter and make your | ||
library use the logging framework of your choice without having to add it as a | ||
dependency for your library. You can add them as | ||
[development dependencies](#install-dev-dependencies-in-a-library-project). | ||
If you have installed an adapter as a dev dependency, you can use that adapter | ||
in your tests so you get the logging the way you like it in your tests. | ||
#### require | ||
#### import | ||
*my-library.test.js* | ||
```js | ||
// e.g. for ulog | ||
require('ulog') | ||
// all anylogger loggers will use ulog | ||
var log = require('anylogger')('my-lbrary:test') | ||
// e.g. for anylogger-console | ||
import 'anylogger-console' | ||
// all loggers will now use the console | ||
import anylogger from 'anylogger' | ||
const log = anylogger('my-lbrary:test') | ||
log.info('Logging is easy!') | ||
``` | ||
#### import | ||
#### require | ||
*my-library.test.js* | ||
```js | ||
// e.g. for ulog | ||
import 'ulog' | ||
// all anylogger loggers will use ulog | ||
import anylogger from 'anylogger' | ||
// e.g. for anylogger-console | ||
require('anylogger-console') | ||
// all loggers will now use the console | ||
const anylogger = require('anylogger') | ||
const log = anylogger('my-lbrary:test') | ||
log.info('Logging is easy!') | ||
``` | ||
### Include in an application project | ||
### Use in an application project | ||
In your main entry point, include your adapter or library with native support | ||
so it extends anylogger: | ||
Install anylogger, your logging framework of choice and an adapter | ||
if needed as regular dependencies. Then, in your main entry point, | ||
include the adapter along with anylogger: | ||
#### require | ||
#### import | ||
*main.js* | ||
```js | ||
// e.g. for debug | ||
require('anylogger-debug') | ||
// all anylogger loggers will use debug | ||
// e.g. for anylogger-console | ||
import adapter from 'anylogger-console' | ||
import anylogger from 'anylogger' | ||
adapter(anylogger) | ||
// all loggers will now use the console | ||
const log = anylogger('my-app') | ||
log.info('Logging is easy!') | ||
``` | ||
#### import | ||
#### require | ||
*main.js* | ||
```js | ||
// e.g. for debug | ||
import 'anylogger-debug' | ||
// all anylogger loggers will use debug | ||
// e.g. for anylogger-console | ||
const adapter = require('anylogger-console') | ||
const anylogger = require('anylogger') | ||
adapter(anylogger) | ||
// all loggers will now use the console | ||
const log = anylogger('my-app') | ||
log.info('Logging is easy!') | ||
``` | ||
In your other modules, use only anylogger and restrict yourself to the | ||
[Anylogger API](#anylogger-api) to stay framework-independent: | ||
In your other application modules, use only anylogger and restrict yourself to | ||
the [Anylogger API](#anylogger-api) to stay framework-independent: | ||
#### require | ||
#### import | ||
*my-module.js* | ||
```js | ||
var log = require('anylogger')('my-module') | ||
import anylogger from 'anylogger' | ||
const log = anylogger('my-module') | ||
log.info('Logging is easy!') | ||
``` | ||
#### import | ||
#### require | ||
*my-module.js* | ||
```js | ||
import anylogger from 'anylogger' | ||
const anylogger = require('anylogger') | ||
const log = anylogger('my-module') | ||
log.info('Logging is easy!') | ||
``` | ||
By limiting yourself to the anylogger API, you ensure that even the code in | ||
your aplication modules remains logging framework independent. That way, | ||
you can easily turn an application module into a library later should the | ||
need arise. Or should you want to switch logging libraries, you will be able | ||
to do that by only changing that one single line that imports the adapter | ||
in the entrypoint in *main.js* or whatever your main file is called. | ||
## Using anylogger | ||
## Using anylogger for logging | ||
@@ -362,9 +421,20 @@ Anylogger is very natural to use: | ||
```js | ||
var log = require('anylogger')('my-module') | ||
import anylogger from 'anylogger' | ||
const log = anylogger('my-module') | ||
// the log object is itself a function | ||
// this increases interop with `debug` | ||
log('A log message') | ||
log('debug', 'A debug message') | ||
log('warn', 'A warning message') | ||
log.info(log.name + ' starting...') | ||
log.error('Something went wrong', new Error('Oh no!')) | ||
// the log object also has methods, | ||
// increasing interop with the console | ||
log.info('Starting...') | ||
log.warn('Watch out!') | ||
log.error('Oh no!! Something went wrong') | ||
// the log API supports the concept of levels | ||
Object.keys(anylogger.levels) | ||
// > ['error', 'warn', 'info', 'log', 'debug', 'trace'] | ||
if (log.enabledFor('warn')) { | ||
@@ -375,11 +445,3 @@ log.warn(expensiveArguments()) | ||
If you are able to restrict yourself to the [Anylogger API](#anylogger-api), | ||
your code will be framework independent and will work with any supported | ||
logging library. | ||
```js | ||
log.info('Logging is easy!') | ||
``` | ||
## Anylogger API | ||
@@ -392,34 +454,32 @@ | ||
```js | ||
function anylogger(name, options) => logger | ||
function anylogger(name: string) => Logger | ||
``` | ||
The main function to call to get a logger. | ||
Accepts two arguments. | ||
Accepts the logger name as argument. | ||
#### name | ||
The name of the logger. String. Optional. Defaults to `undefined`. | ||
The name of the logger. String. | ||
The recommended format is `<package-name>[:<sub>[:<sub> [..]]]`, | ||
as this is the [convention](https://www.npmjs.com/package/debug#conventions) | ||
used by the highly popular `debug` module. But you are free to pick any name | ||
you want. You only get a logger if you supply a name. If the name is | ||
not given `anylogger()` will return an object containing all loggers, | ||
keyed by name. | ||
used by the highly popular `debug` module. But you are free to pick | ||
any name you want as long as you steer clear of the characters `'*'`, | ||
`'='` and `';'` in the logger name. | ||
#### options | ||
An optional options object. Object. Optional. Defaults to `undefined`. | ||
The use of such options objects varies wildly amongst implementations so | ||
it is recommended to avoid using it where possible. However in case of | ||
implementations that require it, anylogger passes any options object it | ||
is given on to [`anylogger.new`](#anyloggernew) to allow it to be used | ||
where needed. | ||
#### returns | ||
The existing logger with that name, or a newly created one. | ||
**When no arguments are given** anylogger returns an object containing | ||
all loggers created so far, keyed by name. | ||
The returned logger adheres to the `Logger` type: | ||
**When a name is given** anylogger returns the existing logger with that | ||
name, or creates a new one by calling [`anylogger.new`](#anyloggernew). | ||
```ts | ||
The returned logger adheres to the Logging API described below. | ||
``` | ||
This means it is a | ||
`LogFunction` and a `LogObject` all at once. The `LogFunction` aspect makes | ||
anylogger loggers compatible with `debug` out of the box, whereas the | ||
`LogObject` aspect makes them compatible with the console and most other | ||
logging libraries. I call that a Win-Win! | ||
## Logging API | ||
@@ -609,3 +669,3 @@ | ||
Please have a look at the | ||
[source](https://unpkg.com/anylogger@1.1.0-beta.0/anylogger.js) | ||
[source](https://unpkg.com/anylogger@1.1.0-beta.1/anylogger.js) | ||
it should make it more clear how to write an adapter. Also consider studying | ||
@@ -612,0 +672,0 @@ the [available adapters](https://www.npmjs.com/search?q=keywords:anylogger) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
52600
725
694
0