@discomedia/utils 1.0.10 → 1.0.11

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