UNPKG

@neodx/log

Version:

A lightweight universal logging framework

178 lines (171 loc) 6.18 kB
/* eslint-disable @typescript-eslint/no-explicit-any */ const toArray = value => Array.isArray(value) ? value : [value]; const toInt = value => Number.parseInt(value, 10); const is = target => value => target === value; const identity = value => value; const isTruthy = Boolean; const True = () => true; const setMapValue = (map, key, value) => { map.set(key, value); return value; }; const getOrCreateMapValue = (map, key, create) => map.has(key) ? map.get(key) : setMapValue(map, key, create()); //#region Object const values = Object.values; const fromEntries = Object.fromEntries; const entries = Object.entries; const hasOwn = Object.hasOwn; const keys = Object.keys; //#endregion const toString = Object.prototype.toString; const getPrototypeOf = Object.getPrototypeOf; const objectString = '[object Object]'; const isEmpty = target => target.length === 0; const isError = target => target instanceof Error; const isEmptyObject = target => isEmpty(keys(target)); const createTypeof = type => value => typeof value === type; const isTypeOfString = createTypeof('string'); const isTypeOfFunction = createTypeof('function'); const isPrimitive = value => value === null || (typeof value !== 'function' && typeof value !== 'object'); const isObject = target => { if (isNil(target) || !isObjectLike(target) || toString.call(target) !== objectString) { return false; } const proto = getPrototypeOf(target); return proto === null || proto === getLastPrototypeOf(target); }; const isNil = target => target == null; const isObjectLike = target => typeof target === 'object' && target !== null; const getLastPrototypeOf = target => { const proto = getPrototypeOf(target); return proto === null ? target : getLastPrototypeOf(proto); }; function mapValues(target, fn) { return mapEntries(target, ([key, value]) => [key, fn(value, key)]); } function mapEntries(target, fn) { return fromEntries(entries(target).map(fn)); } const createCase = pattern => (input, delimiters = DEFAULT_DELIMITERS) => toCase(input, pattern, delimiters); /** * Converts a string to a specified case pattern. * @param input The string to convert. * @param pattern The pattern to convert to. * @param delimiters The delimiters to use when splitting the string. * @example toCase('foo-bar baz', 'CaSe') // PascalCase 'FooBarBaz' * @example toCase('foo_bar baz', 'caSe') // camelCase 'fooBarBaz' * @example toCase('foo_bar baz', 'CASE') // upper case 'FOOBARBAZ' * @example toCase('foo_bar baz', 'case') // lower case 'foobarbaz' * @example toCase('foo_bar baz', 'CA_SE') // screaming snake case 'FOO_BAR_BAZ' * @example toCase('foo_bar baz', 'ca-se') // kebab case 'foo-bar-baz' * @example toCase('foo_bar baz', 'ca_se') // snake case 'foo_bar_baz' * @example toCase('foo_bar baz', 'Case') // capitalised 'Foobarbaz' */ function toCase(input, pattern, delimiters = DEFAULT_DELIMITERS) { const end = pattern.slice(Math.max(0, pattern.length - 2)); const sep = pattern.slice(2, -2); const start = pattern.slice(0, 2); return matches(input, delimiters) .map((match, index) => applyPattern(match, index === 0 ? start : end)) .join(sep); } /** * Presets for common cases. */ mapValues( { camel: 'caSe', kebab: 'ca-se', snake: 'ca_se', upper: 'CASE', lower: 'case', pascal: 'CaSe', capital: 'Case', screamingSnake: 'CA_SE' }, createCase ); const applyPattern = (str, pattern) => apply(pattern[0], str[0]) + apply(pattern[1], str.slice(1)); function matches(str, delimiters) { const regex = new RegExp('([A-Z]?)([^' + delimiters + ']*)', 'g'); return str.match(regex)?.filter(Boolean) ?? []; } function apply(letter, str) { if (letter === '-') return ''; if (letter === '*') return str; const isUpperCase = letter === letter.toUpperCase(); return isUpperCase ? str.toUpperCase() : str.toLowerCase(); } const DEFAULT_DELIMITERS = 'A-Z\\s_-'; const createLoggerAutoFactory = factory => (log, defaultParams) => { const params = isTypeOfString(log) ? { level: log } : log; return 'level' in params ? factory({ ...defaultParams, ...params }) : params; }; /** * Reads arguments array and extract fields, error and message arguments. * @return [messageFragments, fields, error] * * Strings * @example readArguments('hello') -> [ ['hello'], {} ] * @example readArguments('hello %s', 'world') -> [ ['hello %s', 'world'], {} ] * @example readArguments('hello %s %d %j', 'world', 1, { id: 2 }) -> [ ['hello %s %d %j', 'world', 1, { id: 2 }], {} ] * * Additional fields * @example readArguments({ id: 2 }) -> [ [], { id: 2 } ] * @example readArguments({ id: 2 }, 'hello') -> [ ['hello'], { id: 2 } ] * @example readArguments({ id: 2 }, 'hello %s', 'world') -> [ ['hello %s', 'world'], { id: 2 } ] * * Errors * @example readArguments(myError) -> [ ['my error'], {}, myError ] * @example readArguments({ err: myError }) -> [ ['my error'], {}, myError ] * @example readArguments({ err: myError, id: 2 }) -> [ ['my error'], { id: 2 }, myError ] * @example readArguments({ err: myError, id: 2 }, 'hello') -> [ ['hello'], { id: 2 }, myError ] * @example readArguments({ err: myError, id: 2 }, 'hello %s', 'world') -> [ ['hello %s', 'world'], { id: 2 }, myError ] */ function readArguments(args) { const [firstArg, ...otherArgs] = args; if (isError(firstArg)) { return [isEmpty(otherArgs) ? [firstArg.message] : otherArgs, {}, firstArg]; } if (isObjectLike(firstArg)) { if ('err' in firstArg && isError(firstArg.err)) { const { err, ...fields } = firstArg; return [isEmpty(otherArgs) ? [err.message] : otherArgs, fields, err]; } return [otherArgs, firstArg]; } return [args, {}]; } export { True as T, isEmptyObject as a, isTruthy as b, createLoggerAutoFactory as c, isTypeOfFunction as d, isEmpty as e, is as f, isTypeOfString as g, entries as h, identity as i, getOrCreateMapValue as j, keys as k, toInt as l, isPrimitive as m, hasOwn as n, isObject as o, readArguments as r, toArray as t, values as v }; //# sourceMappingURL=read-arguments-BklHPmep.mjs.map