@discomedia/utils 1.0.10 → 1.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -9882,6 +9882,128 @@ const makeDeepseekCall = async (content, responseFormat = 'json', options = {})
9882
9882
  }
9883
9883
  };
9884
9884
 
9885
+ /**
9886
+ * A class to measure performance of code execution.
9887
+ *
9888
+ * This utility records elapsed time during execution and provides a detailed
9889
+ * breakdown of elapsed time between checkpoints.
9890
+ *
9891
+ * Usage example:
9892
+ * const timer = new PerformanceTimer();
9893
+ * timer.checkpoint('Start Processing');
9894
+ * // ... code for processing
9895
+ * timer.checkpoint('After Task 1');
9896
+ * // ... additional processing code
9897
+ * timer.stop();
9898
+ * console.log(timer.generateReport());
9899
+ *
9900
+ * Methods:
9901
+ * - checkpoint(label: string): Record a timestamped checkpoint.
9902
+ * - stop(): Stop the timer and compute the total elapsed time.
9903
+ * - generateReport(): Return a performance report with total time and phases.
9904
+ * - formatPerformanceReport(): Return a formatted string of the performance report.
9905
+ * - getElapsedSeconds(): Get the current elapsed time in seconds.
9906
+ */
9907
+ class PerformanceTimer {
9908
+ startTime;
9909
+ checkpoints;
9910
+ totalTime;
9911
+ constructor() {
9912
+ this.startTime = performance.now();
9913
+ this.checkpoints = new Map();
9914
+ this.totalTime = null;
9915
+ }
9916
+ /**
9917
+ * Gets the current elapsed time in seconds.
9918
+ *
9919
+ * @returns The elapsed time in seconds with 3 decimal places of precision.
9920
+ */
9921
+ getElapsedSeconds() {
9922
+ const elapsedMs = this.totalTime ?? (performance.now() - this.startTime);
9923
+ return Number((elapsedMs / 1000).toFixed(3));
9924
+ }
9925
+ /**
9926
+ * Records a checkpoint with the given label.
9927
+ *
9928
+ * @param label - A descriptive label for the checkpoint.
9929
+ */
9930
+ checkpoint(label) {
9931
+ this.checkpoints.set(label, performance.now());
9932
+ }
9933
+ /**
9934
+ * Stops the timer and sets the total elapsed time.
9935
+ */
9936
+ stop() {
9937
+ this.totalTime = performance.now() - this.startTime;
9938
+ }
9939
+ /**
9940
+ * Generates a performance report containing the total elapsed time and
9941
+ * breakdown of phases between checkpoints.
9942
+ *
9943
+ * @returns A performance report object.
9944
+ * @throws Error if the timer is not stopped before generating the report.
9945
+ */
9946
+ analyseReportData() {
9947
+ if (this.totalTime === null) {
9948
+ throw new Error('Timer must be stopped before generating report');
9949
+ }
9950
+ const report = {
9951
+ totalTime: this.totalTime,
9952
+ phases: []
9953
+ };
9954
+ let lastTime = this.startTime;
9955
+ // Convert checkpoints to a sorted array by time
9956
+ const sortedCheckpoints = Array.from(this.checkpoints.entries())
9957
+ .sort((a, b) => a[1] - b[1]);
9958
+ for (const [label, time] of sortedCheckpoints) {
9959
+ const duration = time - lastTime;
9960
+ if (duration < 0) {
9961
+ throw new Error(`Negative duration detected for checkpoint: ${label}`);
9962
+ }
9963
+ report.phases.push({
9964
+ label,
9965
+ duration
9966
+ });
9967
+ lastTime = time;
9968
+ }
9969
+ // Add final phase from last checkpoint to stop
9970
+ if (sortedCheckpoints.length > 0) {
9971
+ const finalDuration = this.totalTime - lastTime;
9972
+ report.phases.push({
9973
+ label: 'Final Processing',
9974
+ duration: Math.max(finalDuration, 0) // Prevent negative duration
9975
+ });
9976
+ }
9977
+ return report;
9978
+ }
9979
+ /**
9980
+ * Returns a formatted string of the performance report.
9981
+ *
9982
+ * @returns A string detailing the total execution time and phase breakdown.
9983
+ */
9984
+ generateReport() {
9985
+ const report = this.analyseReportData();
9986
+ // Format numbers with thousands separators and 2 decimal places
9987
+ const formatNumber = (num) => num.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
9988
+ // Calculate column widths
9989
+ const maxLabelLength = Math.max(...report.phases.map(p => p.label.length), 30);
9990
+ const maxDurationLength = Math.max(...report.phases.map(p => formatNumber(p.duration).length), 10);
9991
+ // Create table header
9992
+ let output = `╔${'═'.repeat(maxLabelLength + 2)}╦${'═'.repeat(maxDurationLength + 2)}╗\n`;
9993
+ output += `║ ${'Phase'.padEnd(maxLabelLength)} ║ ${'Time (ms)'.padEnd(maxDurationLength)} ║\n`;
9994
+ output += `╠${'═'.repeat(maxLabelLength + 2)}╬${'═'.repeat(maxDurationLength + 2)}╣\n`;
9995
+ // Add table rows
9996
+ report.phases.forEach(phase => {
9997
+ output += `║ ${phase.label.padEnd(maxLabelLength)} ║ ${formatNumber(phase.duration).padStart(maxDurationLength)} ║\n`;
9998
+ });
9999
+ // Add table footer with total
10000
+ output += `╠${'═'.repeat(maxLabelLength + 2)}╬${'═'.repeat(maxDurationLength + 2)}╣\n`;
10001
+ output += `║ ${'Total'.padEnd(maxLabelLength)} ║ ${formatNumber(report.totalTime).padStart(maxDurationLength)} ║\n`;
10002
+ output += `╚${'═'.repeat(maxLabelLength + 2)}╩${'═'.repeat(maxDurationLength + 2)}╝\n`;
10003
+ return output;
10004
+ }
10005
+ }
10006
+
9885
10007
  /**
9886
10008
  * Calculates Bollinger Bands for a given set of price data.
9887
10009
  * Bollinger Bands consist of a middle band (SMA) and two outer bands
@@ -18081,6 +18203,7 @@ const disco = {
18081
18203
  logIfDebug: logIfDebug,
18082
18204
  fetchWithRetry: fetchWithRetry,
18083
18205
  validatePolygonApiKey: validatePolygonApiKey,
18206
+ Timer: PerformanceTimer,
18084
18207
  },
18085
18208
  };
18086
18209