All files / src/lib/formatters meta-table.ts

87.5% Statements 14/16
81.25% Branches 13/16
100% Functions 4/4
87.5% Lines 14/16

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                              11x                                           129x     129x 64x 4x   60x     60x   65x                                         29x 85x 85x 85x   85x   129x            
/**
 * Generic table-rendering helpers driven by Sentry Events API
 * `meta.fields` / `meta.units` metadata.
 *
 * Used by `sentry explore` (and any future command rendering dynamic
 * Discover/Events results) to format cell values according to the
 * field's declared type and unit, and to build right-aligned columns
 * for numeric fields.
 */
 
import { escapeMarkdownCell } from "./markdown.js";
import { appendUnitSuffix, formatNumber } from "./numbers.js";
import type { Column } from "./table.js";
 
/** Sentry field types that render as right-aligned numeric columns. */
export const NUMERIC_FIELD_TYPES = new Set([
  "integer",
  "number",
  "duration",
  "percentage",
  "size",
]);
 
/**
 * Format a single cell value according to its `meta.fields` type.
 *
 * - `null` / `undefined` → `"—"`
 * - `duration` / `size` → `formatNumber` + unit suffix (`"1,234ms"`, `"5MB"`)
 * - `percentage` → multiplied by 100 and suffixed with `%`
 * - other numbers → `formatNumber` (locale grouping, K/M/B above 1M)
 * - non-numeric → `String(value)` with markdown-cell escaping
 */
export function formatCellValue(
  value: unknown,
  fieldType?: string,
  unit?: string | null
): string {
  Iif (value === null || value === undefined) {
    return "—";
  }
  if (typeof value === "number") {
    if (fieldType === "duration" || fieldType === "size") {
      return appendUnitSuffix(formatNumber(value), unit);
    }
    Iif (fieldType === "percentage") {
      return `${formatNumber(value * 100)}%`;
    }
    return formatNumber(value);
  }
  return escapeMarkdownCell(String(value));
}
 
/**
 * Build dynamic table columns from API response field metadata.
 *
 * Each field name becomes a column. Numeric fields (per
 * {@link NUMERIC_FIELD_TYPES}) are right-aligned and not truncated.
 *
 * Cell values are extracted from `row[name]` and formatted via
 * {@link formatCellValue}.
 *
 * @param fieldNames - Column order (typically the user's `--field` order)
 * @param fieldTypes - Optional `meta.fields` map: field name → Sentry type
 * @param fieldUnits - Optional `meta.units` map: field name → unit string
 */
export function buildMetaColumns(
  fieldNames: string[],
  fieldTypes?: Record<string, string>,
  fieldUnits?: Record<string, string | null>
): Column<Record<string, unknown>>[] {
  return fieldNames.map((name) => {
    const fieldType = fieldTypes?.[name];
    const unit = fieldUnits?.[name];
    const isNumeric = fieldType ? NUMERIC_FIELD_TYPES.has(fieldType) : false;
 
    return {
      header: name.toUpperCase(),
      value: (row) => formatCellValue(row[name], fieldType, unit),
      align: isNumeric ? ("right" as const) : ("left" as const),
      truncate: !isNumeric,
    };
  });
}