All files / src/utils/common duration.ts

100% Statements 9/9
100% Branches 11/11
100% Functions 3/3
100% Lines 9/9

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125                                                            48x 251x   251x             251x 251x                                                                   48x 16x                                                                                           48x 264x  
/**
 * Returns a string representation of the duration.
 *
 * The returned stringified number for the duration is guaranteed to be non-negative and is formatted as milliseconds.
 * By default, the number of decimal places adapts to the magnitude of the value to avoid noisy or misleading precision
 * in logs:
 *
 * - Values below `100` include `2` decimal places
 * - Values below `1000` include `1` decimal place
 * - Values `>= 1000` are rendered without a fractional part
 *
 * A fixed decimal precision can be enforced via the `precision` option. By default, the `"ms"` unit suffix is included.
 *
 * @example
 *
 * ```ts
 * import { stringifyDuration } from 'emitnlog/utils';
 *
 * stringifyDuration(12.345); // "12.35ms"
 * stringifyDuration(123.45); // "123.5ms"
 * stringifyDuration(2500.34); // "2500ms"
 *
 * stringifyDuration(2500.34, { precision: 2 }); // "2500.34ms"
 * stringifyDuration(12.345, { suppressUnit: true }); // "12.35"
 * ```
 *
 * @param duration The duration, typically obtained from `performance.now() - start`.
 * @param options Optional formatting options.
 * @returns A string representing the duration.
 */
export const stringifyDuration = (duration: number, options?: StringifyDurationOptions): string => {
  const value = Math.max(0, duration);
  const precision =
    options?.precision !== undefined
      ? toNonNegativeInteger(options.precision, 2)
      : value < 100
        ? 2
        : value < 1000
          ? 1
          : 0;
  const text = value.toFixed(precision);
  return options?.suppressUnit ? text : `${text}ms`;
};
 
/**
 * Returns a string representation of the elapsed time, in milliseconds, since the specified performance start.
 *
 * The elapsed time is computed using `performance.now()` and is guaranteed to be non-negative. The result is formatted
 * with a configurable decimal precision and, by default, includes the `"ms"` unit suffix. By default, the number of
 * decimal places adapts to the magnitude of the value to avoid noisy or misleading precision in logs:
 *
 * - Elapsed time below `100` include `2` decimal places
 * - Elapsed time below `1000` include `1` decimal place
 * - Elapsed time `>= 1000` are rendered without a fractional part
 *
 * A fixed decimal precision can be enforced via the `precision` option. By default, the `"ms"` unit suffix is included.
 *
 * @example
 *
 * ```ts
 * import { stringifyElapsed } from 'emitnlog/utils';
 *
 * const start = performance.now();
 *
 * // ... do some work ...
 *
 * stringifyElapsed(start); // "12.34ms"
 * stringifyElapsed(start, { precision: 0 }); // "12ms"
 * stringifyElapsed(start, { suppressUnit: true }); // "12.34"
 * ```
 *
 * @param performanceStart The start timestamp, typically obtained from `performance.now()`.
 * @param options Optional formatting options.
 * @returns A string representing the elapsed time in milliseconds.
 */
export const stringifyElapsed = (performanceStart: number, options?: StringifyDurationOptions): string =>
  stringifyDuration(performance.now() - Math.max(0, performanceStart), options);
 
type StringifyDurationOptions = {
  /**
   * Number of decimal places to include.
   *
   * The default value varies based on the magnitude of the duration:
   *
   * - Duration below `100` include `2` decimal places
   * - Duration below `1000` include `1` decimal place
   * - Duration `>= 1000` are rendered without a fractional part
   *
   * @default 2, 1, or 0
   */
  readonly precision?: number;
 
  /**
   * When true, omits the `"ms"` unit suffix.
   *
   * @default false
   */
  readonly suppressUnit?: boolean;
};
/**
 * Returns a non negative integer for the specified value.
 *
 * If the value is undefined, returns the specified `defaultValue`. If lower than 0, the return is zero. Otherwise the
 * return is the number, rounded down to the nearest integer.
 *
 * @example
 *
 * ```ts
 * import { toNonNegativeInteger } from 'emitnlog/utils';
 *
 * const value = toNonNegativeInteger(undefined); // 0
 * const value = toNonNegativeInteger(undefined, 2); // 2
 * const value = toNonNegativeInteger(undefined, -2); // 0
 * const value = toNonNegativeInteger(1.5); // 1
 * const value = toNonNegativeInteger(-1, 2); // 0
 * const value = toNonNegativeInteger(10); // 10
 * ```
 *
 * @param value
 * @param [defaultValue] The default value to be used if the value is undefined
 * @returns An integer greater or equal than zero, _regardless_ of the default value.
 */
export const toNonNegativeInteger = (value: number | undefined, defaultValue = 0): number =>
  value === undefined ? Math.max(0, Math.floor(defaultValue)) : Math.max(0, Math.floor(value));