UNPKG

stellar-plus

Version:

beta version of stellar-plus, an all-in-one sdk for the Stellar blockchain

200 lines (199 loc) 9.87 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ProfilingHandler = void 0; const tslib_1 = require("tslib"); class ProfilingHandler { constructor() { this.log = []; this.resourceHandler = (methodName, costs, elapsedTime, feeCharged) => { const entry = { methodName, costs, feeCharged, elapsedTime, }; this.log.push(entry); }; this.getLog = (options) => { const filteredLog = (options === null || options === void 0 ? void 0 : options.filter) ? this.filterLog(this.log, options.filter) : this.log; const aggregatedLog = (options === null || options === void 0 ? void 0 : options.aggregate) ? this.aggregateLog(filteredLog, options.aggregate) : filteredLog; if (options === null || options === void 0 ? void 0 : options.clear) { this.clearLog(); } if ((options === null || options === void 0 ? void 0 : options.formatOutput) && options.formatOutput === 'csv') { return this.formatAsCsv(aggregatedLog); } if ((options === null || options === void 0 ? void 0 : options.formatOutput) && options.formatOutput === 'text-table') { return this.formatAsTable(aggregatedLog); } return aggregatedLog; }; this.clearLog = () => { this.log = []; }; // apply filters to log and return filtered log // filter by methods, resources and values this.filterLog = (log, filters) => { const { methods } = filters, resources = tslib_1.__rest(filters, ["methods"]); const filteredLogByMethods = methods ? this.filterLogByMethods(log, methods) : log; const filteredLogByResources = resources ? this.filterLogByResources(filteredLogByMethods, resources) : filteredLogByMethods; return filteredLogByResources; }; this.filterLogByMethods = (log, methods) => { return log.filter((entry) => methods.includes(entry.methodName)); }; this.filterLogByResources = (log, resources) => { return log.reduce((acc, logEntry) => { let shouldIncludeLogEntry = true; const filteredCosts = Object.keys(logEntry.costs).reduce((costsAccumulator, resourceKey) => { const resourceFilter = resources[resourceKey]; const resourceValue = logEntry.costs[resourceKey]; if (!resourceFilter) { // Rule 1 and 5: Include the resource as is costsAccumulator[resourceKey] = resourceValue; } else if (resourceFilter.include === false) { // Rule 2: Exclude this resource from costs, but keep the log entry // No action needed here, as the resource is simply not added to costsAccumulator } else { // Rule 3 and 4: Check min/max only if include is true or undefined const withinMinRange = resourceFilter.min === undefined || resourceValue >= resourceFilter.min; const withinMaxRange = resourceFilter.max === undefined || resourceValue <= resourceFilter.max; if (typeof resourceValue === 'number' && withinMinRange && withinMaxRange) { costsAccumulator[resourceKey] = resourceValue; } else { shouldIncludeLogEntry = false; // Exclude the entire log entry } } return costsAccumulator; }, {}); if (shouldIncludeLogEntry) { acc.push(Object.assign(Object.assign({}, logEntry), { costs: filteredCosts })); } return acc; }, []); }; this.aggregateLog = (log, aggregateOptions) => { var _a; const costs = []; const resources = Object.keys(((_a = log[0]) === null || _a === void 0 ? void 0 : _a.costs) || {}); resources.forEach((resourceKey) => { const specificMethod = aggregateOptions[resourceKey]; const defaultMethod = aggregateOptions.all; const aggregationMethod = specificMethod || defaultMethod; if (aggregationMethod) { const aggregatedValue = this.performAggregation(log, resourceKey, aggregationMethod); costs.push([resourceKey, aggregatedValue]); } }); let elapsedTime; if (aggregateOptions.elapsedTime) { elapsedTime = this.performAggregation(log, 'elapsedTime', aggregateOptions.elapsedTime); } let feeCharged; if (aggregateOptions.feeCharged) { feeCharged = this.performAggregation(log, 'feeCharged', aggregateOptions.feeCharged); } return [ Object.assign({ methodName: 'aggregated', costs: Object.fromEntries(costs) }, { elapsedTime, feeCharged }), ]; }; this.performAggregation = (logEntries, resourceKey, aggregationMethod) => { const values = logEntries.map((entry) => { const value = resourceKey === 'elapsedTime' ? entry.elapsedTime : resourceKey === 'feeCharged' ? entry.feeCharged : entry.costs[resourceKey]; return typeof value === 'number' ? value : 0; }); switch (aggregationMethod.method) { case 'sum': return values.reduce((acc, value) => acc + value, 0); case 'average': { const total = values.reduce((acc, value) => acc + value, 0); return total / values.length; } case 'standardDeviation': { const mean = values.reduce((acc, value) => acc + value, 0) / values.length; const squaredDifferences = values.map((value) => (value - mean) ** 2); const variance = squaredDifferences.reduce((acc, value) => acc + value, 0) / values.length; const standardDeviation = Math.sqrt(variance); return standardDeviation; } // Implement other aggregation methods like median, etc. default: throw new Error(`Unknown aggregation method: ${aggregationMethod.method}`); } }; } formatAsTable(log) { var _a; if (log.length === 0) { return ''; // Return an empty string if the log is empty } // Determine the column widths const columnWidths = []; const headers = ['Method Name', 'Elapsed Time', 'Fee Charged', ...Object.keys(((_a = log[0]) === null || _a === void 0 ? void 0 : _a.costs) || {})]; headers.forEach((header, index) => { const maxLength = Math.max(header.length, ...log.map((entry) => { var _a, _b; return Math.max(entry.methodName.length, ((_a = entry.elapsedTime) === null || _a === void 0 ? void 0 : _a.toString()).length, entry.feeCharged.toString().length, (((_b = entry.costs[header]) === null || _b === void 0 ? void 0 : _b.toString()) || '').length); })); columnWidths[index] = maxLength; }); // Generate the table header const headerRow = headers.map((header, index) => header.padEnd(columnWidths[index])).join(' | '); // Generate the separator row const separatorRow = columnWidths.map((width) => '-'.repeat(width)).join('-+-'); // Generate the data rows const dataRows = log .map((entry) => { var _a; const row = [ entry.methodName, (_a = entry.elapsedTime) === null || _a === void 0 ? void 0 : _a.toString().padEnd(columnWidths[1]), entry.feeCharged.toString().padEnd(columnWidths[2]), ...headers.slice(3).map((header) => (entry.costs[header] || '') .toString() .padEnd(columnWidths[headers.indexOf(header) + 3])), ]; return row.join(' | '); }) .join('\n'); // Combine the header, separator, and data rows const formattedTable = [headerRow, separatorRow, dataRows].join('\n'); return formattedTable; } formatAsCsv(log) { var _a; if (log.length === 0) { return ''; // Return an empty string if the log is empty } // Generate the header row const headers = ['Method Name', 'Elapsed Time', 'Fee Charged', ...Object.keys(((_a = log[0]) === null || _a === void 0 ? void 0 : _a.costs) || {})]; const headerRow = headers.join(','); // Generate the data rows const dataRows = log .map((entry) => { const row = [ entry.methodName, entry.elapsedTime, entry.feeCharged, ...headers .slice(3) .map((header) => (entry.costs[header] || '').toString()), ]; return row.join(','); }) .join('\n'); // Combine the header and data rows const formattedCsv = [headerRow, dataRows].join('\n'); return formattedCsv; } } exports.ProfilingHandler = ProfilingHandler;