@report-toolkit/transformers
Version:
See docs at [https://ibm.github.io/report-toolkit](https://ibm.github.io/report-toolkit)
999 lines (864 loc) • 26.3 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var common = require('@report-toolkit/common');
var json2csv = require('json2csv');
var stripAnsi = _interopDefault(require('strip-ansi'));
var stringify = _interopDefault(require('fast-safe-stringify'));
var hashjs = _interopDefault(require('hash.js'));
var CLITable3 = _interopDefault(require('cli-table3'));
var wrapAnsi = _interopDefault(require('wrap-ansi'));
/**
* CSV transformer. Accepts an object or {@link Report}, and outputs CSV.
* @module @report-toolkit/transformers.csv
*/
const {
filter,
concatMapTo,
finalize,
fromEvent,
map,
takeUntil,
tap
} = common.observable;
/**
* @type {TransformerMeta}
*/
const meta = {
description: 'Comma-separated values',
id: 'csv',
input: ['object', 'report'],
output: 'string'
};
/**
* CSV transformer; accepts whatever json2csv can handle
* @see https://npm.im/json2csv
* @param {CSVTransformOptions} [parserOpts]
* @type {TransformFunction<string|object,CSVTransformResult>}
*/
const transform = (parserOpts = {}) => observable => {
// XXX: the parser wants to add a newline to everything we push to it for
// some reason. likely "user error"
const parser = new json2csv.AsyncParser({ ...parserOpts,
eol: '',
flatten: true
}, {
objectMode: true
});
return observable.pipe(finalize(() => {
parser.input.push(null);
}), tap(row => {
parser.input.push(row);
}), concatMapTo(fromEvent(parser.processor, 'data')), takeUntil(fromEvent(parser.processor, 'end')), filter(Boolean), map(common._.pipe(common._.trim, stripAnsi)));
};
/**
* This is a single CSV-formatted row
* @typedef {string} CSVTransformResult
*/
/**
* Options for AsyncParser
* @typedef {object} CSVTransformOptions
*/
/**
* @typedef {import('@report-toolkit/common').Report} Report
* @typedef {import('./transformer.js').TransformerMeta} TransformerMeta
* @typedef {import('./transformer.js').TransformerField} TransformerField
*/
/**
* @template T,U
* @typedef {import('./transformer.js').TransformFunction<T,U>} TransformFunction
*/
var csv = /*#__PURE__*/Object.freeze({
__proto__: null,
meta: meta,
transform: transform
});
/**
* A general-purpose filtering Transformer, which allows whitelisting or
* blacklisting of properties from output.
* @module @report-toolkit/transformers.filter
*/
const {
map: map$1
} = common.observable;
const debug = common.createDebugPipe('transformer', 'filter');
/**
* @type {TransformerMeta}
*/
const meta$1 = {
description: 'Filters properties',
id: 'filter',
input: ['report'],
output: 'object'
};
/**
* @param {FilterTransformerOptions} opts
* @type {TransformFunction<object,object>}
*/
const transform$1 = ({
include = [],
exclude = []
} = {}) => {
/**
* @type {Function[]}
*/
const filterFns = [common._.identity];
if (include.length) {
filterFns.push(common._.pick(include));
}
if (exclude.length) {
filterFns.push(common._.omit(exclude));
}
const filterFn = common._.pipe.apply(null, filterFns);
return observable => observable.pipe(map$1(filterFn), debug(data => [`filtered data: %O`, data]));
};
/**
* @typedef {import('@report-toolkit/common').Report} Report
* @typedef {import('./transformer.js').TransformerMeta} TransformerMeta
* @typedef {{include?: string[], exclude?: string[]}} FilterTransformerOptions
*/
/**
* @template T,U
* @typedef {import('./transformer.js').TransformFunction<T,U>} TransformFunction
*/
var filter$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
meta: meta$1,
transform: transform$1
});
/**
* JSON transformer; outputs JSON.
* @module @report-toolkit/transformers.json
*/
const {
map: map$2,
toArray,
pipeIf,
mergeAll
} = common.observable;
const debug$1 = common.createDebugPipe('transformers', 'json');
/**
* @type {TransformerMeta}
*/
const meta$2 = {
defaults:
/**
* @type {Partial<JSONTransformOptions>}
*/
{
pretty: false
},
description: 'JSON',
id: 'json',
input: ['string', 'object', 'number', 'report'],
output: 'string'
};
/**
* Emits a single JSON blob.
* @param {Partial<JSONTransformOptions>} [opts]
* @type {TransformFunction<object,string>}
*/
const transform$2 = ({
pretty = false
} = {}) => observable => observable.pipe(toArray(), pipeIf(result => result.length === 1, mergeAll()), debug$1(values => [`transforming to JSON with pretty = ${pretty}`, values]), map$2(pretty ? values => stringify(values, null, 2) : values => stringify(values)));
/**
* @typedef {object} JSONTransformOptions
* @property {TransformerField[]} fields
* @property {boolean} pretty
*/
/**
* @typedef {import('@report-toolkit/common').Report} Report
* @typedef {import('./transformer.js').TransformerMeta} TransformerMeta
* @typedef {import('./transformer.js').TransformerField} TransformerField
*/
/**
* @template T,U
* @typedef {import('./transformer.js').TransformFunction<T,U>} TransformFunction
*/
var json = /*#__PURE__*/Object.freeze({
__proto__: null,
meta: meta$2,
transform: transform$2
});
/**
* A transformer which outputs newline-delimited output. Can be used to output
* newline-delimited JSON ("ndjson").
* @module @report-toolkit/transformers.newline
*/
const {
concatMap,
of
} = common.observable;
/**
* @type {TransformerMeta}
*/
const meta$3 = {
defaults:
/**
* @type {NewlineTransformOptions}
*/
{
json: true
},
description: 'Newline-delimited output',
id: 'newline',
input: ['string', 'object', 'number'],
output: 'string'
};
/**
* Newline parser; given whatever, output a string ending with newline (or `newline` of your choice)
* @param {Partial<NewlineTransformOptions>} [opts]
* @type {TransformFunction<any,string>}
*/
const transform$3 = ({
json = meta$3.defaults.json
} = {}) => observable => observable.pipe(concatMap(value => of(json || common._.isObject(value) ? stringify(value) : String(value))));
/**
* @typedef {object} NewlineTransformOptions
* @property {boolean} json - If true, force-stringify the value. Objects will always be stringified
*/
/**
* @typedef {import('@report-toolkit/common').Report} Report
* @typedef {import('./transformer.js').TransformerMeta} TransformerMeta
* @typedef {import('./transformer.js').TransformerField} TransformerField
*/
/**
* @template T,U
* @typedef {import('./transformer.js').TransformFunction<T,U>} TransformFunction
*/
var newline = /*#__PURE__*/Object.freeze({
__proto__: null,
meta: meta$3,
transform: transform$3
});
/**
* Transformer which redacts secrets from a {@link Report}. This transformer is
* _always_ run unless the user explicitly disables it.
* @module @report-toolkit/transformers.redact
*/
const {
map: map$3
} = common.observable;
/**
* @type {TransformerMeta}
*/
const meta$4 = {
description: 'Redact secrets from a report',
id: 'redact',
input: ['report'],
output: 'report'
};
/**
* @type {TransformFunction<Report,Report>}
*/
const transform$4 = (opts = {}) => observable => observable.pipe(map$3(report => common.redact(report, opts)));
/**
* @typedef {import('@report-toolkit/common').Report} Report
* @typedef {import('./transformer.js').TransformerMeta} TransformerMeta
*/
/**
* @template T,U
* @typedef {import('./transformer.js').TransformFunction<T,U>} TransformFunction
*/
var redact = /*#__PURE__*/Object.freeze({
__proto__: null,
meta: meta$4,
transform: transform$4
});
/**
* @module @report-toolkit/transformers.stack-hash
*/
const {
map: map$4
} = common.observable;
/**
* @type {TransformerMeta}
*/
const meta$5 = {
defaults:
/** @type {StackHashTransformOptions} */
{
fields: [{
label: 'SHA1',
value: 'sha1'
}, {
label: 'Time',
value: 'dumpEventTime'
}, {
label: 'File',
value: 'filepath'
}, {
label: 'Error',
value: 'message'
}, {
label: 'Stack',
value: 'stack'
}],
strip: /[0-9]+/g
},
description: 'Generate unique hash for JS stack trace',
id: 'stack-hash',
input: ['report'],
output: 'object'
};
/**
* Given a report, generate a SHA1 hash of the stack trace. Useful when
* determining whether a stack trace is new or already known.
* @param {Partial<StackHashTransformOptions>} [opts] - Options
* @type {TransformFunction<Report,StackHashTransformResult>}
*/
const transform$5 = ({
strip
} = {}) => observable => observable.pipe(map$4(report => {
// @ts-ignore
const {
dumpEventTime
} = report.header;
const {
filepath
} = report; // @ts-ignore
const {
message,
stack
} = report.javascriptStack;
const strippedMessage = common._.isFunction(strip) ?
/** @type {((arg: string) => string)} */
strip(message) : message.replace(strip, '');
return {
dumpEventTime,
filepath,
message: strippedMessage,
sha1: hashjs.sha1().update(`${strippedMessage}${stack.join(',')}`).digest('hex'),
stack
};
}));
/**
* @typedef {{dumpEventTime:string,filename?:string,sha1:string,message:string}} StackHashTransformResult
* @typedef {{fields: TransformerField[], strip: RegExp|function(string):string|string}} StackHashTransformOptions
*/
/**
* @typedef {import('@report-toolkit/common').Report} Report
* @typedef {import('./transformer.js').TransformerMeta} TransformerMeta
* @typedef {import('./transformer.js').TransformerField} TransformerField
*/
/**
* @template T,U
* @typedef {import('./transformer.js').TransformFunction<T,U>} TransformFunction
*/
var stackHash = /*#__PURE__*/Object.freeze({
__proto__: null,
meta: meta$5,
transform: transform$5
});
var version = "0.6.1";
/**
* @module @report-toolkit/transformers.table
*/
const debug$2 = common.createDebugPipe('transformers', 'table');
const {
concatMap: concatMap$1,
from,
map: map$5,
pipeIf: pipeIf$1,
reduce
} = common.observable;
const DEFAULT_TABLE_OPTS = {
chars: {
bottom: '',
'bottom-left': '',
'bottom-mid': '',
'bottom-right': '',
left: '',
'left-mid': '',
mid: '─',
'mid-mid': '',
middle: '',
right: '',
'right-mid': '',
top: '',
'top-left': '',
'top-mid': '',
'top-right': ''
},
/**
* @type {number[]}
*/
colWidthPcts: [],
/**
* @type {Field[]}
*/
fields: [],
style: {
/**
* @type {object[]}
*/
head: []
},
wordWrap: true
};
const fieldWidthPcts = common._.pipe(common._.map('widthPct'), common._.map(Number));
/**
* @param {(...args: any[]) => string | string} v
*/
const constantValue = v => common._.isFunction(v) ? v : common._.constant(v);
/**
* @param {(arg: CLITable3.Table) => string | string} header
* @param {CLITable3.Table} value
* @returns {string}
*/
const withHeader = (header, value) => {
header = constantValue(header);
return common.colors.grey('[') + common.colors.cyan().bold('report-toolkit') + ' ' + common.colors.cyan(`v${version}`) + common.colors.grey('] ') + common.colors.magenta(header(value)) + `
`;
};
/**
* @param {(arg: CLITable3.Table) => string | string} footer
* @param {CLITable3.Table} value
* @returns {string}
*/
const withFooter = (footer, value) => {
/**
* @type {(arg: CLITable3.Table) => string}
*/
footer = constantValue(footer);
return `
${footer(value)}`;
};
const safeSum = common._.reduce((sum, pct) => common._.isNaN(pct) ? sum : sum + pct, 0);
/**
* This little nasty accepts a list of field objects with `widthPct`
* props and a `colWidths` array. It prefers the `colWidths` array.
* It calculates column widths _as a percentage_ of maxWidth.
* @todo "Infinity" should be a problem
* @param {Field[]} fields
* @param {number[]} colWidths
* @param {number} maxWidth
* @returns {number[]}
*/
const normalizeColWidthPcts = (fields, colWidths, maxWidth) => {
if (!common._.isEmpty(colWidths)) {
const maxPct = 100 - safeSum(colWidths) / maxWidth * 100;
const fieldsCount = common._.size(fields) - common._.size(common._.filter(common._.isNumber, colWidths));
return common._.map(width => typeof width === 'number' ? Math.floor(width / maxWidth * 100) : Math.floor(maxPct / fieldsCount), [...colWidths, ...new Array(fields.length - colWidths.length).fill(null)]);
}
const colWidthPcts = fieldWidthPcts(fields);
if (common._.some(common._.isNaN, colWidthPcts)) {
const maxPct = 100 - safeSum(colWidthPcts);
if (maxPct < 100) {
const fieldsCount = common._.size(fields) - common._.size(common._.filter(common._.isNumber, colWidthPcts));
return common._.map(pct => common._.isNaN(pct) ? Math.floor(maxPct / fieldsCount) : pct, colWidthPcts);
} else {
const fieldsCount = common._.size(fields);
return new Array(fieldsCount).fill(Math.floor(100 / fieldsCount));
}
}
return colWidthPcts;
};
/**
*
* @param {Field[]} [fields] - Field settings
* @param {number[]} [colWidths] - Fixed column widths
* @param {number} [maxWidth] - Maximum column width
* @returns {number[]}
*/
const calculateColumnWidths = (fields = [], colWidths = [], maxWidth = 80) => common._.map( // normalize column widths to total max width
pct => Math.floor(pct / 100 * maxWidth), // normalize column widths based on explicit colWidths option
normalizeColWidthPcts(fields, colWidths, maxWidth));
/**
* @param {Field[]} fields - Field whose table headers need formatting
* @returns {string[]} Formatted headers
*/
const formatTableHeaders = common._.pipe(common._.map('label'), common._.map(v => common.colors.underline(v)));
/**
*
* @param {Object} [opts] - Options
* @returns {CLITable3.Table}
*/
const createTable = (opts = {}) => {
opts = common._.defaultsDeep(DEFAULT_TABLE_OPTS, opts);
const {
fields,
maxWidth,
truncate,
colWidths
} = opts;
if (truncate) {
opts.colWidths = calculateColumnWidths(fields, colWidths, maxWidth);
}
return new CLITable3({ // "truncate" is used by CLITable3 for the truncation symbol.
...common._.omit('truncate', opts),
head: formatTableHeaders(fields),
truncate: '…'
});
};
const colValuesByFields = common._.curry(
/**
* @param {Field[]} fields
* @param {object} row
* @returns {string[]}
*/
(fields, row) => common._.map(common._.invokeArgs('value', [row]), fields));
/**
* @type {TransformerMeta}
*/
const meta$6 = {
description: 'Tabular output',
id: 'table',
input: ['object'],
output: 'string'
};
/**
* @returns {TransformFunction<object,string>}
*/
const transform$6 = (opts = {}) => {
const table = createTable(opts);
const {
fields,
outputFooter,
outputHeader,
wrap
} = opts;
const colValues = colValuesByFields(fields);
const padding = table.options.style['padding-left'] - table.options.style['padding-right'];
return observable => observable.pipe(debug$2(
/**
* @param {object} value
*/
// @ts-ignore
value => [`received data %O`, value]), map$5(colValues), pipeIf$1(wrap, map$5( // this force-wraps the column text
common._.map(
/**
* @param {string} col
* @param {string | number} idx
*/
(col, idx) => wrapAnsi(col, table.options.colWidths[idx] - padding, {
hard: true,
wordWrap: false
})))), reduce((table, row) => {
// `push` must be used because Table subclasses Array, but
// doesn't implement concat, so we'd just get a plain Array back...
table.push(row);
return table;
}, table), concatMap$1(table => {
const isTableEmpty = common._.isEmpty(table);
/**
* @type {Array<string|CLITable3.Table>}
*/
const output = isTableEmpty ? [] : [table];
if (!isTableEmpty) {
if (outputHeader) {
output.unshift(withHeader(outputHeader, table));
}
if (outputFooter) {
output.push(withFooter(outputFooter, table));
}
}
return from(output);
}), map$5(String));
};
/**
* @typedef {import('@report-toolkit/common').Report} Report
* @typedef {import('./transformer.js').TransformerField} Field
* @typedef {import('./transformer.js').TransformerMeta} TransformerMeta
*/
/**
* @template T,U
* @typedef {import('./transformer.js').TransformFunction<T,U>} TransformFunction
*/
var table = /*#__PURE__*/Object.freeze({
__proto__: null,
meta: meta$6,
transform: transform$6
});
/**
* @module @report-toolkit/transformers
*/
const {
RTKERR_INVALID_TRANSFORMER_PIPE,
createRTkError
} = common.error;
const FIELD_COLORS = Object.freeze(['cyan', 'magenta', 'blue', 'green']);
const debug$3 = common.createDebugger('transformers', 'transformer');
/**
* @type {Partial<TransformerMeta>}
*/
const DEFAULT_TRANSFORMER_META = Object.freeze({
alias: [],
input: ['report']
});
const configMap = new WeakMap();
/**
* Represents a Transformer having a transform() function and
* metadata.
* @template T,U
*/
class Transformer {
/**
* Sets defaults and instance props
* @param {TransformFunction<T,U>} transform
* @param {TransformerMeta} meta - Transformer metdata
* @param {object} [config] - Transformer config
*/
constructor(transform, meta, config = {}) {
this._meta = common._.defaultsDeep(DEFAULT_TRANSFORMER_META, meta);
this._transform = transform;
if (config.fields) {
config.fields = Transformer.normalizeFields(config.fields);
}
configMap.set(this, config);
debug$3(`created Transform with id "%s" and config %O`, this._meta.id, config);
}
get id() {
return this._meta.id;
}
get input() {
return this._meta.input;
}
get output() {
return this._meta.output;
}
get defaults() {
return this._meta.defaults;
}
/**
* Pipe one Transformer to another
* @param {Transformer<any,any>} transformer
* @returns {Transformer<any,any>}
*/
pipe(transformer) {
if (!this.canPipeTo(transformer)) {
throw createRTkError(RTKERR_INVALID_TRANSFORMER_PIPE, `Transformer "${this.id}" cannot pipe to transformer "${transformer.id}"`);
}
return transformer.pipeFrom(this);
}
transform() {
let defaults = [this.defaults];
if (this._source && configMap.has(this._source)) {
const sourceOpts = configMap.get(this._source);
defaults = common._.defaults({
fields: sourceOpts.fields
}, defaults);
}
return this._transform(common._.defaultsDeep(defaults, configMap.get(this)));
}
/**
*
* @param {Transformer<any,any>} transformer
* @returns {Transformer<any,any>}
*/
pipeFrom(transformer) {
this._source = transformer;
return this;
}
/**
* Returns `true` if this Transformer can pipe to another
* @param {Transformer<any,any>} transformer - Transformer to compare
*/
canPipeTo(transformer) {
return common._.includes(this.output, transformer.input);
}
canBeginWith(type) {
return common._.includes(type, this.input);
}
canEndWith(type) {
return this.output === type;
}
/**
* Creates a Transformer
* @template T,U
* @param {TransformFunction<T,U>} transform - Transformer function
* @param {TransformerMeta} meta - Transformer meta
* @param {object} [config] - Transformer config
* @returns {Transformer<T,U>}
*/
static create(transform, meta, config = {}) {
return new Transformer(transform, meta, config);
}
}
Transformer.normalizeFields = common._.pipe(common._.toPairs, common._.map(
/**
* @param {[number, TransformerField]} value
*/
([idx, field]) => {
// a field can have a string `color`, no `color`, or a function which accepts a `row` and returns a string.
// likewise, it can have a `value` function which accepts a `row` and returns a value, or just a string, which
// corresponds to a property of the `row` object.
const fieldColor = field.color || FIELD_COLORS[idx % FIELD_COLORS.length];
const colorFn = common._.isFunction(fieldColor) ? (row, value) => {
// the function might not return a color
const result =
/** @type {((arg0: any) => string)} */
fieldColor(row);
const color = common.colors[result] ? result : FIELD_COLORS[idx % FIELD_COLORS.length];
return common.colors[color](value);
} : (row, value) => common.colors[
/** @type {string} */
fieldColor](value);
const valueFn = common._.isFunction(field.value) ? row => {
// yuck
const fn =
/**
* @type {function(typeof row): string}
*/
field.value;
return fn(row);
} : common._.get(field.value);
return { ...field,
value: row => colorFn(row, valueFn(row))
};
}));
const createTransformer = Transformer.create;
/**
* @typedef {"json"|"csv"|"table"} Formatters
* @typedef {{label: string, value: string|function(any): string, color?: string|function(any): string}} TransformerField
* @typedef {import('@report-toolkit/common').Report} Report
*/
/**
* @typedef {{id: string, description?: string, input?: string[], output: string, alias?: string[], defaults?: any}} TransformerMeta
*/
/**
* @template T,U
* @typedef {import('rxjs/internal/types').OperatorFunction<T,U>} OperatorFunction<T,U>
*/
/**
* @template T,U
* @typedef {(opts?: object)=>OperatorFunction<T,U>} TransformFunction
*/
const builtinTransformers = [csv, filter$1, json, newline, redact, stackHash, table];
const transformerModules = common._.fromPairs(common._.map(transformer => [transformer.meta.id, transformer], builtinTransformers));
const DEFAULT_TRANSFORMER = 'table';
const {
RTKERR_INVALID_TRANSFORMER_HEAD,
RTKERR_UNKNOWN_TRANSFORMER,
createRTkError: createRTkError$1
} = common.error;
const {
concatMap: concatMap$2,
map: map$6,
mergeAll: mergeAll$1,
pipeIf: pipeIf$2,
share,
switchMap,
tap: tap$1,
toArray: toArray$1,
throwRTkError
} = common.observable;
const debug$4 = common.createDebugPipe('transformers');
/**
* @type {Readonly<string[]>}
*/
const builtinTransformerIds = Object.freeze(common._.map('meta.id', builtinTransformers));
/**
* @param {string} id - Transformer ID
* @param {object} [config] - Configuration
*/
const loadTransformer = (id, config = {}) => {
if (!isKnownTransformer(id)) {
throw createRTkError$1(RTKERR_UNKNOWN_TRANSFORMER, `Unknown transformer "${id}"`);
}
const {
meta,
transform
} = transformerModules[id];
return createTransformer(
/** @type {TransformFunction<any,any>} */
transform, meta, config);
};
/**
* @param {string} id
*/
const isKnownTransformer = id => Boolean(transformerModules[id]);
/**
* @returns {OperatorFunction<TransformerConfig,Transformer<any,any>>}
*/
const toTransformer = () => observable => observable.pipe(pipeIf$2(({
id
}) => !isKnownTransformer(id), switchMap(({
id
}) => throwRTkError(RTKERR_UNKNOWN_TRANSFORMER, `Unknown transformer "${id}"`))), map$6(({
id,
config
}) => loadTransformer(id, config)));
/**
*
* @param {Partial<TransformOptions>} [opts]
* @returns {OperatorFunction<Transformer<any,any>,Transformer<any,any>>}
*/
const validateTransformerChain = ({
beginWith = 'report',
endWith = 'string',
defaultTransformer = DEFAULT_TRANSFORMER,
defaultTransformerConfig = {}
} = {}) => observable => observable.pipe(toArray$1(), debug$4(transformers => [`validating chain of transformers: %O`, common._.map('id', transformers)]), tap$1(
/**
* @param {Transformer[]} transformers
*/
transformers => {
let idx = 0;
let transformer = transformers[idx];
if (!transformer.canBeginWith(beginWith)) {
// TODO: list valid transformers (using URL?)
throw createRTkError$1(RTKERR_INVALID_TRANSFORMER_HEAD, `The first transformer ("${transformer.id}") must accept a "${beginWith}"`);
}
if (!transformers[transformers.length - 1].canEndWith(endWith)) {
transformers.push(loadTransformer(defaultTransformer, defaultTransformerConfig));
}
let nextTransformer = transformers[++idx];
while (nextTransformer) {
transformer = transformer.pipe(nextTransformer);
nextTransformer = transformers[++idx];
}
return transformers;
}), debug$4(transformers => `transformer pipe {${beginWith}} => ${common._.map('id', transformers).join(' => ')} => {${endWith}} OK`), mergeAll$1());
/**
* @param {Observable<any>} source
*/
const runTransformer = source =>
/**
* @param {Observable<Transformer<any,any>>} observable
*/
observable => observable.pipe(toArray$1(), debug$4(transformers => `running transform(s): ${common._.map('id', transformers).join(' => ')}`), concatMap$2(transformers => // @ts-ignore
source.pipe(share(), ...common._.map(transformer => transformer.transform(), transformers))));
/**
* Returns a list of transformers which can accept data of type `sourceType`
* @todo memoize
* @todo constants for source types
* @param {string} sourceType
*/
const compatibleTransformers = sourceType => common._.keys(common._.fromPairs(common._.filter(([, transformerModule]) => common._.includes(sourceType, transformerModule.meta.input), common._.toPairs(transformerModules))));
/**
* @typedef {import('./transformer.js').TransformerMeta} TransformerMeta
* @typedef {import('@report-toolkit/common').Report} Report
*/
/**
* @template T,U
* @typedef {import('./transformer.js').TransformFunction<T,U>} TransformFunction
*/
/**
* @template T
* @typedef {import('rxjs').Observable<T>} Observable
*/
/**
* @template T,U
* @typedef {import('rxjs').OperatorFunction<T,U>} OperatorFunction
*/
/**
* @typedef {{id: string, config: object}} TransformerConfig
*/
/**
* for {@link validateTransformerChain}
* @typedef {object} TransformOptions
* @property {string} beginWith - Begin transformer chain with this type
* @property {string} endWith - End transformer chain with this type
* @property {string} defaultTransformer - Default transformer
* @property {object} defaultTransformerConfig - Default transformer config
*/
exports.Transformer = Transformer;
exports.builtinTransformerIds = builtinTransformerIds;
exports.compatibleTransformers = compatibleTransformers;
exports.isKnownTransformer = isKnownTransformer;
exports.loadTransformer = loadTransformer;
exports.runTransformer = runTransformer;
exports.toTransformer = toTransformer;
exports.validateTransformerChain = validateTransformerChain;
//# sourceMappingURL=report-toolkit-transformers.cjs.js.map