@report-toolkit/common
Version:
See docs at [https://ibm.github.io/report-toolkit](https://ibm.github.io/report-toolkit)
1,019 lines (908 loc) • 33.1 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var rxjs = require('rxjs');
var index_js = require('rxjs/operators/index.js');
var castArray = _interopDefault(require('lodash/fp/castArray.js'));
var clamp = _interopDefault(require('lodash/fp/clamp.js'));
var compact = _interopDefault(require('lodash/fp/compact.js'));
var concat = _interopDefault(require('lodash/fp/concat.js'));
var constant = _interopDefault(require('lodash/fp/constant.js'));
var curry = _interopDefault(require('lodash/fp/curry.js'));
var curryN = _interopDefault(require('lodash/fp/curryN.js'));
var defaults = _interopDefault(require('lodash/fp/defaults.js'));
var defaultsDeep = _interopDefault(require('lodash/fp/defaultsDeep.js'));
var defaultsDeepAll = _interopDefault(require('lodash/fp/defaultsDeepAll.js'));
var every = _interopDefault(require('lodash/fp/every.js'));
var filter = _interopDefault(require('lodash/fp/filter.js'));
var flatMap = _interopDefault(require('lodash/fp/flatMap.js'));
var flip = _interopDefault(require('lodash/fp/flip.js'));
var forEach = _interopDefault(require('lodash/fp/forEach.js'));
var fromPairs = _interopDefault(require('lodash/fp/fromPairs.js'));
var get = _interopDefault(require('lodash/fp/get.js'));
var getOr = _interopDefault(require('lodash/fp/getOr.js'));
var gte = _interopDefault(require('lodash/fp/gte.js'));
var has = _interopDefault(require('lodash/fp/has.js'));
var identity = _interopDefault(require('lodash/fp/identity.js'));
var includes = _interopDefault(require('lodash/fp/includes.js'));
var intersection = _interopDefault(require('lodash/fp/intersection.js'));
var invokeArgs = _interopDefault(require('lodash/fp/invokeArgs.js'));
var isArray = _interopDefault(require('lodash/fp/isArray.js'));
var isBoolean = _interopDefault(require('lodash/fp/isBoolean.js'));
var isEmpty = _interopDefault(require('lodash/fp/isEmpty.js'));
var isError = _interopDefault(require('lodash/fp/isError.js'));
var isFunction = _interopDefault(require('lodash/fp/isFunction.js'));
var isNaN = _interopDefault(require('lodash/fp/isNaN.js'));
var isNumber = _interopDefault(require('lodash/fp/isNumber.js'));
var isObject = _interopDefault(require('lodash/fp/isObject.js'));
var isString = _interopDefault(require('lodash/fp/isString.js'));
var isUndefined = _interopDefault(require('lodash/fp/isUndefined.js'));
var join = _interopDefault(require('lodash/fp/join.js'));
var keys = _interopDefault(require('lodash/fp/keys.js'));
var lowerCase = _interopDefault(require('lodash/fp/lowerCase.js'));
var map = _interopDefault(require('lodash/fp/map.js'));
var mapKeys = _interopDefault(require('lodash/fp/mapKeys.js'));
var mapValues = _interopDefault(require('lodash/fp/mapValues.js'));
var memoize = _interopDefault(require('lodash/fp/memoize.js'));
var merge = _interopDefault(require('lodash/fp/merge.js'));
var mergeAll = _interopDefault(require('lodash/fp/mergeAll.js'));
var negate = _interopDefault(require('lodash/fp/negate.js'));
var noop = _interopDefault(require('lodash/fp/noop.js'));
var omit = _interopDefault(require('lodash/fp/omit.js'));
var omitBy = _interopDefault(require('lodash/fp/omitBy.js'));
var once = _interopDefault(require('lodash/fp/once.js'));
var orderBy = _interopDefault(require('lodash/fp/orderBy.js'));
var overEvery = _interopDefault(require('lodash/fp/overEvery.js'));
var overSome = _interopDefault(require('lodash/fp/overSome.js'));
var pick = _interopDefault(require('lodash/fp/pick.js'));
var pickBy = _interopDefault(require('lodash/fp/pickBy.js'));
var pipe = _interopDefault(require('lodash/fp/pipe.js'));
var __ = _interopDefault(require('lodash/fp/placeholder.js'));
var pull = _interopDefault(require('lodash/fp/pull.js'));
var reduce = _interopDefault(require('lodash/fp/reduce.js'));
var reverse = _interopDefault(require('lodash/fp/reverse.js'));
var size = _interopDefault(require('lodash/fp/size.js'));
var some = _interopDefault(require('lodash/fp/some.js'));
var split = _interopDefault(require('lodash/fp/split.js'));
var startsWith = _interopDefault(require('lodash/fp/startsWith.js'));
var sum = _interopDefault(require('lodash/fp/sum.js'));
var tap = _interopDefault(require('lodash/fp/tap.js'));
var toPairs = _interopDefault(require('lodash/fp/toPairs.js'));
var toUpper = _interopDefault(require('lodash/fp/toUpper.js'));
var trim = _interopDefault(require('lodash/fp/trim.js'));
var unary = _interopDefault(require('lodash/fp/unary.js'));
var uniq = _interopDefault(require('lodash/fp/uniq.js'));
var values = _interopDefault(require('lodash/fp/values.js'));
var isPromise = _interopDefault(require('p-is-promise'));
var traverse = _interopDefault(require('traverse'));
var debug$3 = _interopDefault(require('debug'));
var kleur = _interopDefault(require('kleur'));
/**
* Known root properties of a Diagnostic Report. To be processed, an object must have *all* of these properties. The current list _in order_:
* - `header`
* - `javascriptStack`
* - `nativeStack`
* - `javascriptHeap`
* - `resourceUsage`
* - `libuv`
* - `workers` (v2)
* - `environmentVariables`
* - `userLimits`
* - `sharedObjects`
*/
const REPORT_KNOWN_ROOT_PROPERTIES = Object.freeze(['header', 'javascriptStack', 'nativeStack', 'javascriptHeap', 'resourceUsage', 'libuv', 'workers', 'environmentVariables', 'userLimits', 'sharedObjects']);
/**
* "Error" severity. The highest severity, and the default.
*/
const ERROR = 'error';
/**
* "Warning" severity.
*/
const WARNING = 'warning';
/**
* "Info" severity. The lowest severity.
*/
const INFO = 'info';
/**
* Project namespace
*/
const NAMESPACE = 'report-toolkit';
/**
* Short project namespace
*/
const SHORT_NAMESPACE = 'rtk';
/**
* Text to use in display if no filepath found
*/
const NO_FILEPATH = '(no filepath)';
/**
* @hidden
*/
const MULTIPLE_FILEPATHS = '(multiple files)';
/**
* @hidden
*/
const DEFAULT_TRANSFORMER = 'table';
/**
* The string token used to replace secrets when redacting from a report.
*/
const REDACTED_TOKEN = '[REDACTED]';
/**
* @hidden
*/
const DEFAULT_TERMINAL_WIDTH = 80;
/**
* Potential severities of Messages reported by the inspector.
* @enum {number}
*/
const SEVERITIES = Object.freeze({
[ERROR]: 30,
[INFO]: 10,
[WARNING]: 20
});
/**
* Typically useless keypaths to explicitly omit from diffs. Anything here should be more specific than what's in {@link DEFAULT_DIFF_INCLUDE}.
*/
const DEFAULT_DIFF_EXCLUDE = Object.freeze(['header.filename', // typically redundant
'header.dumpEventTime', // always different
'header.dumpEventTimeStamp', // ditto
'header.cpus' // can vary wildly
]);
/**
* Things we explicitly want to show in the diffs. Use {@link DEFAULT_DIFF_EXCLUDE} to exclude any child properties of these.
*/
const DEFAULT_DIFF_INCLUDE = Object.freeze(['header', 'environmentVariables', 'userLimits', 'sharedObjects', 'libuv']);
/**
* Default severity across system
*/
const DEFAULT_SEVERITY = WARNING;
var constants = /*#__PURE__*/Object.freeze({
__proto__: null,
REPORT_KNOWN_ROOT_PROPERTIES: REPORT_KNOWN_ROOT_PROPERTIES,
ERROR: ERROR,
WARNING: WARNING,
INFO: INFO,
NAMESPACE: NAMESPACE,
SHORT_NAMESPACE: SHORT_NAMESPACE,
NO_FILEPATH: NO_FILEPATH,
MULTIPLE_FILEPATHS: MULTIPLE_FILEPATHS,
DEFAULT_TRANSFORMER: DEFAULT_TRANSFORMER,
REDACTED_TOKEN: REDACTED_TOKEN,
DEFAULT_TERMINAL_WIDTH: DEFAULT_TERMINAL_WIDTH,
SEVERITIES: SEVERITIES,
DEFAULT_DIFF_EXCLUDE: DEFAULT_DIFF_EXCLUDE,
DEFAULT_DIFF_INCLUDE: DEFAULT_DIFF_INCLUDE,
DEFAULT_SEVERITY: DEFAULT_SEVERITY
});
/**
* Represents a "known" Error
*/
class RTkError extends Error {
/**
* Assigns custom props
* @param {string} code - Error code
* @param {string} message - Error message
* @param {Partial<RTkErrorOptions>} [opts] - Options
*/
constructor(message, code, opts = {}) {
super(message);
this.code = code;
const {
data,
url
} = opts;
this.data = data;
this.url = url;
}
/**
* Create a RTkError
* @param {string} [code] - Error code
* @param {string} [message] - Error message
* @param {Partial<RTkErrorOptions>} opts - Options
*/
static create(code = RTKERR_UNKNOWN_ERROR, message = '(unknown error)', opts = {}) {
return new RTkError(message, code, opts);
}
}
const RTKERR_INVALID_CLI_OPTION = 'RTKERR_INVALID_CLI_OPTION';
const RTKERR_INVALID_CONFIG = 'RTKERR_INVALID_CONFIG';
const RTKERR_INVALID_PARAMETER = 'RTKERR_INVALID_PARAMETER';
const RTKERR_INVALID_REPORT = 'RTKERR_INVALID_REPORT';
const RTKERR_INVALID_RULE_CONFIG = 'RTKERR_INVALID_RULE_CONFIG';
const RTKERR_INVALID_RULE_DEFINITION = 'RTKERR_INVALID_RULE_DEFINITION';
const RTKERR_INVALID_SCHEMA = 'RTKERR_INVALID_SCHEMA';
const RTKERR_INVALID_TRANSFORMER_HEAD = 'RTKERR_INVALID_TRANSFORMER_HEAD';
const RTKERR_INVALID_TRANSFORMER_PIPE = 'RTKERR_INVALID_TRANSFORMER_PIPE';
const RTKERR_INVALID_TRANSFORMER_TAIL = 'RTKERR_INVALID_TRANSFORMER_TAIL';
const RTKERR_MISSING_CONFIG = 'RTKERR_MISSING_CONFIG';
const RTKERR_RULE_NAME_COLLISION = 'RTKERR_RULE_NAME_COLLISION';
const RTKERR_UNKNOWN_BUILTIN_CONFIG = 'RTKERR_UNKNOWN_BUILTIN_CONFIG';
const RTKERR_UNKNOWN_ERROR = 'RTKERR_UNKNOWN_ERROR';
const RTKERR_UNKNOWN_TRANSFORMER = 'RTKERR_UNKNOWN_TRANSFORMER';
const createRTkError = RTkError.create;
/**
* @typedef {object} RTkErrorOptions
* @property {any} data - Extra data
* @property {string} url - URL for more information
*/
var error = /*#__PURE__*/Object.freeze({
__proto__: null,
RTkError: RTkError,
RTKERR_INVALID_CLI_OPTION: RTKERR_INVALID_CLI_OPTION,
RTKERR_INVALID_CONFIG: RTKERR_INVALID_CONFIG,
RTKERR_INVALID_PARAMETER: RTKERR_INVALID_PARAMETER,
RTKERR_INVALID_REPORT: RTKERR_INVALID_REPORT,
RTKERR_INVALID_RULE_CONFIG: RTKERR_INVALID_RULE_CONFIG,
RTKERR_INVALID_RULE_DEFINITION: RTKERR_INVALID_RULE_DEFINITION,
RTKERR_INVALID_SCHEMA: RTKERR_INVALID_SCHEMA,
RTKERR_INVALID_TRANSFORMER_HEAD: RTKERR_INVALID_TRANSFORMER_HEAD,
RTKERR_INVALID_TRANSFORMER_PIPE: RTKERR_INVALID_TRANSFORMER_PIPE,
RTKERR_INVALID_TRANSFORMER_TAIL: RTKERR_INVALID_TRANSFORMER_TAIL,
RTKERR_MISSING_CONFIG: RTKERR_MISSING_CONFIG,
RTKERR_RULE_NAME_COLLISION: RTKERR_RULE_NAME_COLLISION,
RTKERR_UNKNOWN_BUILTIN_CONFIG: RTKERR_UNKNOWN_BUILTIN_CONFIG,
RTKERR_UNKNOWN_ERROR: RTKERR_UNKNOWN_ERROR,
RTKERR_UNKNOWN_TRANSFORMER: RTKERR_UNKNOWN_TRANSFORMER,
createRTkError: createRTkError
});
// there is probably a better way, but I don't know what it is.
/**
* @param {object} arg - Object to turn into a map
* @returns {Map<string,any>}
*/
const toMap = pipe(toPairs, pairs => new Map(pairs));
/**
* @param {object} arg - Object to turn into a frozen map
*/
const toFrozenMap = pipe(toMap,
/**
* @param {Map<string,any>} map
*/
map => Object.freeze(map));
const _ = {
__,
castArray,
clamp,
compact,
concat,
constant,
curry,
curryN,
defaults,
defaultsDeep,
defaultsDeepAll,
every,
filter,
flatMap,
flip,
forEach,
fromPairs,
get,
getOr,
gte,
has,
identity,
includes,
invokeArgs,
intersection,
isArray,
isBoolean,
isEmpty,
isError,
isFunction,
isNaN,
isNumber,
isObject,
isPromise,
isString,
isUndefined,
join,
keys,
lowerCase,
map,
mapKeys,
mapValues,
memoize,
merge,
mergeAll,
negate,
noop,
omit,
omitBy,
once,
orderBy,
overEvery,
overSome,
pick,
pickBy,
pipe,
pull,
reduce,
reverse,
size,
some,
split,
startsWith,
sum,
tap,
toFrozenMap,
toMap,
toPairs,
toUpper,
traverse,
trim,
unary,
uniq,
values
};
/**
* Pipes source Observable to one or more Operators if the predicate is truthy.
* If falsy, just returns the source Observable.
* @param {Function|any} predicate - If a function, evaluated with the value from the source Observable. Anything else is evaluated when called.
* @param {...import('rxjs').OperatorFunction<any,any>} operators - More RxJS operators to pipe to
* @returns {import('rxjs').OperatorFunction<any,any>}
*/
const pipeIf = (predicate, ...operators) => {
predicate = _.isFunction(predicate) ? predicate : _.constant(predicate);
return observable => observable.pipe( // @ts-ignore
index_js.mergeMap(v => predicate(v) ? rxjs.of(v).pipe(...operators) : rxjs.of(v)));
};
/**
* Essentially wraps `_.orderBy()`
* @see https://lodash.com/docs/4.17.11#orderBy
* @param {import('lodash').List<any>} iteratee - Any supported LoDash iteratee or list thereof
* @param {import('lodash').Many<boolean|"asc"|"desc">} [direction=asc] - Order in which to sort (`asc` or `desc`) or list thereof, corresponding to each item in `iteratee` (if `iteratee` is a list)
* @returns {import('rxjs').OperatorFunction<any,any>}
*/
const sort = (iteratee = _.identity, direction = 'asc') => observable => observable.pipe(index_js.toArray(), index_js.mergeMap(_.orderBy(iteratee, direction)));
/**
* Recursively explodes any value, an Array, a Promise, a Promise of Arrays, a
* Promise of Arrays of Promises, etc., into a single Observable.
* If the value is an Observable, returns the value.
* Returns `EMPTY` if value is undefined.
* @todo This can probably be done more efficiently using a loop?
* @param {any} value - Probably anything
* @returns {Observable<any>}
*/
const fromAny = value => rxjs.isObservable(value) ? value : rxjs.defer(() => _.overSome([_.isPromise, _.isArray])(value) ? rxjs.from(value).pipe(index_js.concatMap(fromAny)) : _.isUndefined(value) ? rxjs.EMPTY : rxjs.of(value));
/**
* Creates an Observable that emits an RTkError
* @param {string} code - Error code
* @param {string} message - Error message
* @param {Object} [opts] - Extra info
* @param {*} [opts.data] - Extra data
* @param {string} [opts.url] - URL for more info
* @returns {Observable<never>} An Observable emitting a single error
*/
const throwRTkError = (code, message, opts = {}) => rxjs.throwError(createRTkError(code, message, opts));
/**
* A simple operator that parses a JSON string into the resulting JS representation.
* @returns {import('rxjs').OperatorFunction<string,any>}
*/
const toObjectFromJSON = () => observable => observable.pipe(index_js.map(_.unary(JSON.parse)));
var observable = /*#__PURE__*/Object.freeze({
__proto__: null,
pipeIf: pipeIf,
sort: sort,
fromAny: fromAny,
throwRTkError: throwRTkError,
toObjectFromJSON: toObjectFromJSON,
bindNodeCallback: rxjs.bindNodeCallback,
combineLatest: rxjs.combineLatest,
catchError: index_js.catchError,
concat: rxjs.concat,
concatAll: index_js.concatAll,
concatMap: index_js.concatMap,
concatMapTo: index_js.concatMapTo,
count: index_js.count,
defer: rxjs.defer,
defaultIfEmpty: index_js.defaultIfEmpty,
distinct: index_js.distinct,
EMPTY: rxjs.EMPTY,
filter: index_js.filter,
finalize: index_js.finalize,
first: index_js.first,
from: rxjs.from,
fromEvent: rxjs.fromEvent,
iif: rxjs.iif,
interval: rxjs.interval,
isObservable: rxjs.isObservable,
map: index_js.map,
mapTo: index_js.mapTo,
mergeAll: index_js.mergeAll,
mergeMap: index_js.mergeMap,
Observable: rxjs.Observable,
of: rxjs.of,
pluck: index_js.pluck,
reduce: index_js.reduce,
share: index_js.share,
single: index_js.single,
switchMap: index_js.switchMap,
switchMapTo: index_js.switchMapTo,
take: index_js.take,
takeUntil: index_js.takeUntil,
takeWhile: index_js.takeWhile,
tap: index_js.tap,
throwError: rxjs.throwError,
toArray: index_js.toArray
});
const kFlattenedConfig = Symbol('report-toolkit-flattened-config');
const kRedacted = Symbol('report-toolkit-redacted');
const kReport = Symbol('report-toolkit-report');
const kReportFilepath = Symbol('report-toolkit-report-filepath');
const kRuleId = Symbol('report-toolkit-rule-id');
const kRuleInspect = Symbol('report-toolkit-rule-inspect');
const kRuleMeta = Symbol('report-toolkit-rule-meta');
var symbols = /*#__PURE__*/Object.freeze({
__proto__: null,
kFlattenedConfig: kFlattenedConfig,
kRedacted: kRedacted,
kReport: kReport,
kReportFilepath: kReportFilepath,
kRuleId: kRuleId,
kRuleInspect: kRuleInspect,
kRuleMeta: kRuleMeta
});
const NAMESPACE_SEPARATOR = ':';
const APP_NAMESPACE = `${SHORT_NAMESPACE}*`;
const joinDebugNamespace = _.join(NAMESPACE_SEPARATOR);
/**
* Returns a namespace for debug pkg
* @param {string[]} args Strings to append to debug namespace
*/
const getDebugNamespace = (...args) => joinDebugNamespace([SHORT_NAMESPACE, ...args]);
/**
* Creates a `Debugger` instance with proper namespace
* @see https://npm.im/debug
* @param {string[]} args
*/
const createDebugger = (...args) => _.pipe(getDebugNamespace, debug$3)(...args);
/**
* Enables entire debug namespace for this module. Calling this is just like
* setting `DEBUG=RTK*` in the environment.
*/
const enableDebugger = () => {
debug$3.enable(APP_NAMESPACE);
};
/**
* Creates an RxJS debug "operator" using the supplied namespace(s). The
* resulting operator is essentially like
* [tap](https://rxjs.dev/api/operators/tap), except whatever is returned is
* handed to a `Debugger` instance. The resulting operator accepts a single
* function which receives a value from the source `Observable`. You can return
* an array of values to make use `Debugger`'s sprintf-style formatting.
* @param {string[]} args - Zero or more namespace tokens
*/
const createDebugPipe = (...args) => {
const debug = createDebugger(...args);
return (
/**
* @template T
* @param {(arg: T) => any} fn
* @returns {import('rxjs').OperatorFunction<T,T>}
*/
fn => observable => observable.pipe(index_js.tap(value => {
/**
* @type {any[]}
*/
const msg = _.castArray(fn(value));
if (msg.length) {
debug.apply(null, msg);
}
}))
);
};
/**
* The "recommended" config, which can be referenced by its alias, `rtk:recommended`.
* This is _also_ the _default_ config if no config file is used.
* @type {import('../config').ConfigListItem[]}
**/
var config = [{
rules: {
'cpu-usage': true,
'library-mismatch': true,
'long-timeout': true,
'memory-usage': true
}
}];
/**
* @type {import('../config').BuiltinConfigAliases}
*/
var alias = 'rtk:recommended';
var recommended = {
config: config,
alias: alias
};
const recommended$1 = recommended.config;
const debug = createDebugger('common', 'config');
/**
* @type {Config}
*/
const DEFAULT_CONFIG_SHAPE = {
commands: {},
rules: {},
plugins: [],
transformers: {}
};
/**
* Recursively parses a user-supplied config Object into a usable format.
* @param {ExportedConfig} config - A config object
* @param {ExportedConfig} configObjects - Flattened config objects
* @todo Configs must be validated against a config schema in a depth-first
* manner.
* @todo This might make sense to implement as an Object.
* https://gieseanw.wordpress.com/2019/05/10/algorithms-as-objects/
* @returns {Config} Flattened config
*/
const flattenConfig = (config, configObjects = []) => {
if (_.has(kFlattenedConfig, config)) {
debug('Config already processed');
return (
/** @type {Config} */
config
);
}
/**
* This dumb recursive function flattens a config.
* @todo rewrite this as a loop.
* @todo deeper object validation; use AJV probably
* @param {ConfigListItem} value
*/
const flatten = value => {
if (_.has(kFlattenedConfig, value)) {
debug('Config already processed');
return configObjects.push(value);
}
if (_.isString(value) && BUILTIN_CONFIGS.has(value)) {
const builtin = BUILTIN_CONFIGS.get(value);
if (_.isArray(builtin) || _.isString(builtin)) {
return configObjects.push(flattenConfig(builtin, configObjects));
} else {
value = builtin;
}
}
if (_.isString(value)) {
throw RTkError.create(RTKERR_UNKNOWN_BUILTIN_CONFIG, `Unknown builtin config: "${value}".`);
} else if (!Array.isArray(value) && _.isObject(value)) {
if (_.has('config', value)) {
configObjects.push(flattenConfig(
/** @type {ConfigModule} */
value.config, configObjects));
} else {
_.pipe(_.keys, _.forEach(k => {
if (!_.has(k, DEFAULT_CONFIG_SHAPE)) {
throw RTkError.create(RTKERR_INVALID_CONFIG, `Invalid config key found: "${k}"`);
}
}))(value);
configObjects.push(value);
}
} else {
throw RTkError.create(RTKERR_INVALID_CONFIG, `Invalid config value: "${value}"`);
}
};
if (_.isArray(config)) {
config.forEach(flatten);
} else {
flatten(config);
}
/**
* @type {Config}
*/
const retval = _.defaultsDeepAll([..._.reverse(configObjects)]);
debug('flattened config: %O', retval);
return retval;
};
const RECOMMENDED_CONFIG_ALIAS = recommended.alias;
/**
* @type {Map<BuiltinConfigAliases,ExportedConfig>}
* @todo move this
*/
const BUILTIN_CONFIGS = new Map([[RECOMMENDED_CONFIG_ALIAS, recommended$1]]);
/**
* Given a {@link Config}, return a list of rules that are enabled.
* @param {Config} config - Parsed config
* @returns {string[]} List of rule IDs
*/
function filterEnabledRules(config) {
return _.pipe(_.getOr({}, 'rules'), _.toPairs, _.reduce((enabledRules, [ruleName, ruleConfig]) => _.isObject(ruleConfig) && _.get('enabled', ruleConfig) || _.isBoolean(ruleConfig) && ruleConfig ? [ruleName, ...enabledRules] : enabledRules, []), _.tap(ruleIds => {
debug('found %d enabled rule(s)', ruleIds.length);
}) // @ts-ignore -- problem with _.pipe type?
)(config);
}
/**
* Assign the "flattened config" symbol to complete the config.
* Add default fields.
* @param {Partial<Config>} config - Config to mark flattened
* @returns {Config} The "final" config
*/
function normalizeFlattenedConfig(config) {
return (
/** @type {Config} */
{ ..._.merge(DEFAULT_CONFIG_SHAPE, config),
[kFlattenedConfig]: true
}
);
}
/**
* Given an `Observable` of {@link ExportedConfig} arrays, return a single,
* flattened {@link Config} object. Config object will have a
* `kFlattenedConfig` `Symbol` property set to `true`.
* @todo Eliminate extra empty properties
* @returns {import('rxjs').OperatorFunction<ExportedConfig|ConfigListItem, Config>}
*/
function parseConfig() {
return observable => observable.pipe(index_js.map(_.unary(flattenConfig)), index_js.map(normalizeFlattenedConfig));
}
/**
* A list of built-in config aliases. (This is intended to be a union type
* if/when other built-in configs are added.)
* @typedef {"rtk:recommended"} BuiltinConfigAliases
*/
/**
* The exports of a builtin config file.
* @typedef {ConfigModule} BuiltinConfigModule
* @property {BuiltinConfigAliases} alias - Alias (used by builtin configs only)
*/
/**
* The exports of a config file. There should be only one; `config`, which may
* be a {@link Config} or {@link ExportedConfig}.
* @typedef {Object} ConfigModule
* @property {ConfigListItem[]} config - The exported configuration
*/
/**
* A configuration object. Each configuration file exports an
* {@link ExportedConfig} array containing one or more of these.
* @typedef {FlattenedConfigMarker|ConfigProps} Config
*/
/**
* Alias for the `config` prop of a config file or {@link ConfigModule}. See
* {@link ConfigListItem}.
* @typedef {ConfigListItem[]} ExportedConfig
*/
/**
* An item in the `config` prop of a config file or {@link ConfigModule}.
* @typedef {Partial<Config>|ConfigModule|BuiltinConfigAliases} ConfigListItem
*/
/**
* Properties of a {@link Config} object.
* @typedef {Object} ConfigProps
* @property {string[]} plugins - Zero or more plugin module IDs (as one would `require(id)` them)
* @property {Partial<RulesConfig>} rules - Rule-specific configurations, keyed by rule name.
* @property {Partial<TransformerDefaultsConfig>} transformers - Transformer-specific defaults
* @property {Partial<CommandDefaultsConfig>} commands - Command-specific defaults
*/
/**
* The symbol attached to a flattened {@link Config}.
* @typedef {{[kFlattenedConfig]?: true}} FlattenedConfigMarker
*/
/**
* Command-specific defaults. Command-line arguments will always override these.
* @typedef {{[key in CommandName]: object}} CommandDefaultsConfig
*/
/**
* Valid command names for commands (used with {@link CommandDefaultsConfig}).
* @typedef {"inspect"|"redact"|"transform"|"diff"} CommandName
*/
/**
* Per-rule configuration.
* @typedef {{[key: string]: boolean|RuleConfig}} RulesConfig
*/
/**
* Valid severity levels.
* @typedef {"info"|"warning"|"error"} Severity
*/
/**
* Rule-specific configuration. `severity` and `enable` will always be usable,
* otherwise the rest of the properties are defined by the rule.
* @typedef {{[key: string]: any}} RuleConfig
* @property {Severity} severity - Max severity of Messages emitted by this
* rule. Messages with a lower severity will retain the lower severity.
* Defaults to `error`.
* @property {boolean} enable - Defaults to `true`. Useful to temporarily
* disable the rule.
*/
/**
* A valid transformer name.
* @typedef {"table"|"json"|"csv"|"filter"|"newline"|"redact"|"stack-hash"} TransformerName
*/
/**
* Per-transformer defaults, keyed on transformer name.
* @typedef {{[key in TransformerName]: object}} TransformerDefaultsConfig
*/
/**
* @template T
* @typedef {import('./observable').Observable<T>} Observable
*/
var config$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
RECOMMENDED_CONFIG_ALIAS: RECOMMENDED_CONFIG_ALIAS,
BUILTIN_CONFIGS: BUILTIN_CONFIGS,
filterEnabledRules: filterEnabledRules,
normalizeFlattenedConfig: normalizeFlattenedConfig,
parseConfig: parseConfig
});
const debug$1 = createDebugger('common', 'redact');
const AWS_STR = '(AWS|aws|Aws)?_?';
const QUOTE_STR = '("|\')';
const CONNECT_STR = 's*(:|=>|=)s*';
const OPT_QUOTE_STR = `${QUOTE_STR}?`;
const DEFAULT_WHITELIST = [/^sharedObjects/];
const DEFAULT_REDACT_OPTIONS = {
force: false,
match: [],
whitelist: DEFAULT_WHITELIST
};
const SECRETS = [/passw(or)?d/i, /^pw$/, /^pass$/i, /secret/i, /token/i, /api[-._]?key/i, /session[-._]?id/i, /access[-._]?key/i, /private/i, // cloud foundry
/^vcap_services/i, // azure
/^azure_/i, // google cloud
/^google_application_credentials/i, // express
/^connect\.sid$/, // AWS
/(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}/, new RegExp(`${OPT_QUOTE_STR}${AWS_STR}(SECRET|secret|Secret)?_?(ACCESS|access|Access)?_?(KEY|key|Key)${OPT_QUOTE_STR}${CONNECT_STR}${OPT_QUOTE_STR}[A-Za-z0-9/+=]{40}${OPT_QUOTE_STR}`), new RegExp(`${OPT_QUOTE_STR}${AWS_STR}(ACCOUNT|account|Account)_?(ID|id|Id)?${OPT_QUOTE_STR}${CONNECT_STR}${OPT_QUOTE_STR}[0-9]{4}-?[0-9]{4}-?[0-9]{4}${OPT_QUOTE_STR}`)];
/**
* @param {RegExp[]} secrets
* @param {string} value
* @param {string} key
*/
const isSecret = (secrets, value, key) => _.some(matcher => {
return _.isString(matcher) ? key === matcher || value === matcher : matcher.test(key) || matcher.test(value);
}, secrets);
/**
* Recursively redacts strings from a value based on key matching. Does not
* mutate `obj`. Returned value will have a `kRedacted` Symbol key set to
* `true`. Unless option `force` is `true`, any `obj` having this root property
* will be returned w/o modification.
* @param {object} obj - Object whose string values may be redacted
* @param {RedactOptions} [opts] - Options
* @returns {object} `obj` with potentially redacted values
* @see https://npm.im/traverse
*/
const redact = (obj, opts = {}) => {
let {
force,
match,
whitelist
} = _.defaults(DEFAULT_REDACT_OPTIONS, opts);
if (!force && kRedacted in obj) {
debug$1('encountered previously-redacted object; skipping');
return obj;
} // coerce these to array for easier processing
match = _.castArray(match);
const secrets = _.uniq([...match, ...SECRETS]);
let redactedCount = 0;
const result = { // NOTE: this is NOT `Array.prototype.map`
..._.traverse(obj).map(
/**
* @param {string} value
*/
function (value) {
// potential optimization: keys whose keypaths are longer than the current
// path will never match, so we could disregard them if we pre-processed
// the `keys` array further
const keypath = this.path.join('.');
if (keypath && _.every(regex => !regex.test(keypath), whitelist) && isSecret(secrets, value, keypath)) {
this.update(REDACTED_TOKEN);
redactedCount++;
}
}),
[kRedacted]: true
};
debug$1(`redacted ${redactedCount} values`);
return result;
};
/**
* @typedef {Object} RedactOptions
* @property {string|string[]|RegExp|RegExp[]} [match] - Also redact these
* keypaths (e.g., `header.cwd`) or matching values. A matching keypath will
* redact all children; if the value of `match` is `header`, _everything_ in the
* report's `header` prop will be redacted.
* @property {RegExp[]} [whitelist=[/^sharedObjects/]] - Whitelist these keypaths from redaction
* @property {boolean} [force] - If `true`, redact an already-redacted object
* (one which has `[kRedacted]: true` root prop)
*/
const debug$2 = createDebugger('common', 'report');
/**
* Represents a [Diagnostic Report](https://nodejs.org/api/process.html#process_process_report_getreport_err).
*/
class Report {
/**
* Creates shallow copies of root props in `report`; assigns internally-used `Symbol`s.
* @param {import('../diagnostic-report').DiagnosticReport} report - Raw object
* @param {string?} filepath - Original filepath of report, if available. Defaults to {@link NO_FILEPATH}
*/
constructor(report, filepath = NO_FILEPATH) {
if (!Report.isReportLike(report)) {
throw createRTkError(RTKERR_INVALID_REPORT, `Invalid report!`);
}
this.header = { ...report.header
};
this.javascriptStack = { ...report.javascriptStack
};
this.nativeStack = [...report.nativeStack];
this.javascriptHeap = { ...report.javascriptHeap
};
this.resourceUsage = { ...report.resourceUsage
};
this.libuv = [...report.libuv];
this.workers = [...(report.workers || [])];
this.environmentVariables = { ...report.environmentVariables
};
this.userLimits = { ...report.userLimits
};
this.sharedObjects = [...report.sharedObjects];
this[kReportFilepath] = filepath;
this[kReport] = true;
debug$2(`created Report generated on ${this.header.dumpEventTime} w/ filepath ${this[kReportFilepath]}`);
}
/**
* Original filepath of report, if available. Defaults to {@link NO_FILEPATH}.
*/
get filepath() {
return (
/** @type {string} */
this[kReportFilepath]
);
}
/**
* Creates a read-only {@link Report} from a {@link ReportLike} value.
* Use this instead of `new Report()`!
* @param {ReportLike} rawReport
* @param {string} filepath
*/
static create(rawReport, filepath) {
return Object.freeze(new Report(rawReport, filepath));
}
/**
* Returns `true` if the value is an object having a property `report-toolkit-report` `Symbol` with value `true`.
* @param {any} value
*/
static isReport(value) {
// @ts-ignore
return _.isObject(value) && value[kReport] === true;
}
/**
* Returns `true` if `value` has all expected root properties of a Diagnostic Report (as returned by [process.report.getReport()](https://nodejs.org/api/process.html#process_process_report_getreport_err)), or is a {@link Report}.
* @param {any} value
*/
static isReportLike(value) {
if (Report.isReport(value)) {
return true;
}
if (_.isObject(value)) {
// win32 doesn't report 'userLimits'
let propsToCheck = _.get('header.platform', value) === 'win32' ? _.pull('userLimits', REPORT_KNOWN_ROOT_PROPERTIES) : REPORT_KNOWN_ROOT_PROPERTIES;
if (_.getOr(0, 'header.reportVersion', value) < 2) {
propsToCheck = _.pull('workers', propsToCheck);
}
return _.every(key => {
const hasValue = _.has(key, value);
if (!hasValue) {
debug$2(`report is missing prop "${key}"`);
}
return hasValue;
}, propsToCheck);
}
}
}
const createReport = Report.create;
const isReport = Report.isReport;
const isReportLike = Report.isReportLike;
/**
* Either a {@link Report} or an object with all of the required props. See {@link Report.isReportLike}
* @typedef {import('../diagnostic-report').DiagnosticReport|Report} ReportLike
*/
exports.colors = kleur;
exports.Report = Report;
exports._ = _;
exports.config = config$1;
exports.constants = constants;
exports.createDebugPipe = createDebugPipe;
exports.createDebugger = createDebugger;
exports.createReport = createReport;
exports.enableDebugger = enableDebugger;
exports.error = error;
exports.isReport = isReport;
exports.isReportLike = isReportLike;
exports.observable = observable;
exports.redact = redact;
exports.symbols = symbols;
//# sourceMappingURL=report-toolkit-common.cjs.js.map