UNPKG

@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
'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