This module provides utilities for formatting, display, and presentation of data including string transformations, color operations, time/date formatting, internationalization (i18n), and result value formatting for charts and visualizations.
function snakeCaseName(text: string): string;
function hasSpecialCharacters(text: string): boolean;
function friendlyName(text: string): string;
function capitalize(text: string): string;Example:
import { snakeCaseName, friendlyName, capitalize } from '@lightdash/common';
const snake = snakeCaseName('Customer Name');
// Returns: "customer_name"
const friendly = friendlyName('customer_id');
// Returns: "Customer id"
const capitalized = capitalize('hello world');
// Returns: "Hello world"function hexToRGB(hex: string, alpha: number | undefined): string;
function isHexCodeColor(color: string): boolean;
function getInvalidHexColors(colors: string[]): string[];
function cleanColorArray(colors: string[]): string[];
function getReadableTextColor(backgroundColor: string): string;Example:
import {
hexToRGB,
isHexCodeColor,
getInvalidHexColors,
cleanColorArray,
getReadableTextColor
} from '@lightdash/common';
// Convert hex to RGB
const rgb = hexToRGB('#4C8BF5');
// Returns: "rgb(76, 139, 245)"
const rgba = hexToRGB('#4C8BF5', 0.5);
// Returns: "rgba(76, 139, 245, 0.5)"
// Validate hex color
if (isHexCodeColor('#FF5733')) {
// Valid hex color
}
// Get invalid colors from array
const invalid = getInvalidHexColors(['#FF5733', 'not-a-color', '#123']);
// Returns: ['not-a-color']
// Clean color array
const cleaned = cleanColorArray(['#FF5733 ', ' #123456', '']);
// Returns: ['#FF5733', '#123456']
// Get readable text color for background
const textColor = getReadableTextColor('#000000');
// Returns: 'white'Core formatting functions for numbers, dates, timestamps, booleans, and custom formats.
/**
* Array of supported currency codes for currency formatting
*/
const currencies: string[];
/**
* Formats boolean values to "True" or "False" strings
* @param v - Value to format (accepts various truthy representations)
* @returns "True" or "False"
*/
function formatBoolean<T>(v: T): string;
/**
* Type guard checking if value is a valid moment/dayjs input
* @param value - Value to check
* @returns true if value can be used with moment/dayjs
*/
function isMomentInput(value: unknown): value is MomentInput;
/**
* Gets date format string for a time interval
* @param timeInterval - Time interval (default: DAY)
* @returns Date format string (e.g., "YYYY-MM-DD")
*/
function getDateFormat(timeInterval?: TimeFrames): string;
/**
* Formats a date value according to time interval
* @param date - Date to format (string, number, Date, or moment)
* @param timeInterval - Time interval for formatting (default: DAY)
* @param convertToUTC - Whether to convert to UTC (default: false)
* @returns Formatted date string or "NaT" if invalid
*/
function formatDate(
date: MomentInput,
timeInterval?: TimeFrames,
convertToUTC?: boolean
): string;
/**
* Formats a timestamp value with time component
* @param value - Timestamp to format
* @param timeInterval - Time interval (default: MILLISECOND)
* @param convertToUTC - Whether to convert to UTC (default: false)
* @returns Formatted timestamp string or "NaT" if invalid
*/
function formatTimestamp(
value: MomentInput,
timeInterval?: TimeFrames,
convertToUTC?: boolean
): string;
/**
* Gets local time display with timezone
* @param value - Time value to display
* @param showTimezone - Whether to show timezone (default: true)
* @returns Formatted local time string with timezone
*/
function getLocalTimeDisplay(
value: MomentInput,
showTimezone?: boolean
): string;
/**
* Checks if value is NaN when converted to number
* @param value - Value to check
* @returns true if value is NaN or boolean
*/
function valueIsNaN(value: unknown): boolean;
/**
* Type guard for number values
* @param value - Value to check
* @returns true if value is a valid number
*/
function isNumber(value: unknown): value is number;
/**
* Formats a number value with optional custom format
* @param value - Number to format
* @param format - Optional custom format configuration
* @returns Formatted number string
*/
function formatNumberValue(value: number, format?: CustomFormat): string;
/**
* Applies default formatting to any value
* @param value - Value to format
* @returns Formatted string (null → "∅", undefined → "-", numbers formatted)
*/
function applyDefaultFormat(value: unknown): string;
/**
* Applies custom format configuration to a value
* Main formatting function that handles all custom format types
* @param value - Value to format
* @param format - Custom format configuration
* @returns Formatted string according to format type
*/
function applyCustomFormat(
value: unknown,
format?: CustomFormat
): string;
/**
* Formats raw value based on field type
* Primarily handles date/timestamp UTC conversion
* @param field - Field definition (dimension, metric, table calculation, or custom dimension)
* @param value - Raw value from database
* @returns Formatted value (dates/timestamps converted to UTC strings)
*/
function formatRawValue(
field: Field | Metric | TableCalculation | CustomDimension | undefined,
value: AnyType
): AnyType;
/**
* Converts legacy format options to CustomFormat
* @param format - Legacy format type
* @param compact - Compact notation
* @param round - Decimal rounding
* @returns CustomFormat object
*/
function getCustomFormatFromLegacy(options: {
format?: Format | string;
compact?: CompactOrAlias;
round?: number;
}): CustomFormat;
/**
* Checks if item has any formatting options
* @param item - Field, metric, table calculation, or custom dimension
* @returns true if item has formatting
*/
function hasFormatting(
item: Field | AdditionalMetric | TableCalculation | CustomDimension | undefined
): boolean;
/**
* Gets custom format configuration from item
* @param item - Field, metric, table calculation, or custom dimension
* @returns CustomFormat or undefined
*/
function getCustomFormat(
item: Field | AdditionalMetric | TableCalculation | CustomDimension | undefined
): CustomFormat | undefined;
/**
* Formats value using ECMA-376 format expression
* @param expression - Format expression (e.g., "0,0.00", "#,##0.00 \"USD\"")
* @param value - Value to format
* @returns Formatted string
*/
function formatValueWithExpression(expression: string, value: unknown): string;
/**
* Checks if item has valid format expression
* @param item - Field or metric to check
* @returns true if item has valid format expression
*/
function hasValidFormatExpression<
T extends Field | AdditionalMetric | TableCalculation | CustomDimension | Dimension
>(item: T | undefined): item is T & { format: string };
/**
* Converts CustomFormat to ECMA-376 format expression
* @param format - Custom format configuration
* @returns Format expression string or null
*/
function convertCustomFormatToFormatExpression(format: CustomFormat): string | null;
/**
* Gets format expression for item
* @param item - Field or additional metric
* @returns Format expression string or undefined
*/
function getFormatExpression(item: Item | AdditionalMetric): string | undefined;Example:
import {
currencies,
formatBoolean,
formatDate,
formatTimestamp,
formatNumberValue,
getCustomFormat,
applyCustomFormat,
formatValueWithExpression,
convertCustomFormatToFormatExpression,
CustomFormatType,
type CustomFormat,
} from '@lightdash/common';
// Supported currencies
console.log(currencies);
// ['USD', 'EUR', 'GBP', 'JPY', 'DKK', ...]
// Boolean formatting
formatBoolean('true'); // "True"
formatBoolean('1'); // "True"
formatBoolean('false'); // "False"
formatBoolean(0); // "False"
// Date formatting
formatDate('2024-01-15', TimeFrames.DAY);
// "2024-01-15"
formatDate('2024-01-15', TimeFrames.MONTH);
// "2024-01"
formatTimestamp('2024-01-15T10:30:45', TimeFrames.MINUTE);
// "2024-01-15, 10:30 (Z)"
// Number formatting with custom format
const currencyFormat: CustomFormat = {
type: CustomFormatType.CURRENCY,
currency: 'USD',
round: 2,
separator: NumberSeparator.COMMA_PERIOD,
};
formatNumberValue(1234.56, currencyFormat);
// "$1,234.56"
// Percentage formatting
const percentFormat: CustomFormat = {
type: CustomFormatType.PERCENT,
round: 1,
};
applyCustomFormat(0.1234, percentFormat);
// "12.3%"
// Note: prefix, suffix, and compact properties are defined in CustomFormat
// but are not currently implemented in the formatting functions.
// Use the 'currency' property for currency symbols, and values will be
// formatted with full precision according to round and separator settings.
// Format expressions (ECMA-376/Excel format strings)
formatValueWithExpression('0,0.00', 1234.56);
// "1,234.56"
formatValueWithExpression('"Revenue: $"#,##0', 5000);
// "Revenue: $5,000"
// Convert custom format to expression
const format: CustomFormat = {
type: CustomFormatType.CURRENCY,
currency: 'EUR',
round: 2,
};
const expression = convertCustomFormatToFormatExpression(format);
// "[$€]#,##0.00"
// Get format from field
const field = {
name: 'revenue',
type: DimensionType.NUMBER,
formatOptions: currencyFormat,
};
const fieldFormat = getCustomFormat(field);
// Returns currencyFormat
// Check if field has formatting
if (hasFormatting(field)) {
const formatted = applyCustomFormat(value, getCustomFormat(field));
}
// Apply custom format directly
const percentValue = applyCustomFormat(0.1234, {
type: CustomFormatType.PERCENT,
round: 2,
});
// Returns: "12.34%"
const currencyValue = applyCustomFormat(1234.56, {
type: CustomFormatType.CURRENCY,
currency: 'USD',
round: 2,
});
// Returns: "$1,234.56"
// Format raw values (handles date UTC conversion)
const dateField: Dimension = {
fieldType: FieldType.DIMENSION,
type: DimensionType.DATE,
name: 'created_date',
// ... other properties
};
const rawDate = new Date('2024-01-15T10:30:00Z');
const formattedRaw = formatRawValue(dateField, rawDate);
// Returns: "2024-01-15T10:30:00.000Z" (UTC string)Functions for parsing date and time strings into Date objects, and utilities for working with custom week start days.
/**
* Parses date string according to time interval format
* @param str - Date string to parse (format depends on timeInterval)
* @param timeInterval - Time interval for parsing (default: DAY)
* @returns Parsed Date object
*/
function parseDate(str: string, timeInterval?: TimeFrames): Date;
/**
* Parses timestamp string according to time interval format
* @param str - Timestamp string to parse (format depends on timeInterval)
* @param timeInterval - Time interval for parsing (default: MILLISECOND)
* @returns Parsed Date object
*/
function parseTimestamp(str: string, timeInterval?: TimeFrames): Date;
/**
* Gets local time display with timezone information
* @param value - Time value to display (string, number, Date, or moment)
* @param showTimezone - Whether to show timezone (default: true)
* @returns Formatted local time string in "YYYY-MM-DD HH:mm (Timezone)" format
*/
function getLocalTimeDisplay(
value: MomentInput,
showTimezone?: boolean
): string;
/**
* Converts WeekDay enum to moment.js week day format
* Lightdash uses 0-6 (Monday-Sunday), moment.js uses 0-6 (Sunday-Saturday)
* @param weekDay - WeekDay enum value (0=Monday to 6=Sunday)
* @returns Moment.js week day number (0=Sunday to 6=Saturday)
*/
function convertWeekDayToMomentWeekDay(weekDay: WeekDay): number;
/**
* Creates moment.Moment with custom week start day
* Used for accurate week-based date operations with custom start of week
* @param startOfWeek - Custom start of week day (WeekDay enum, null, or undefined)
* @param inp - Optional moment input (date string, number, Date, etc.)
* @returns moment.Moment instance with custom locale if startOfWeek is provided
*/
function getMomentDateWithCustomStartOfWeek(
startOfWeek: WeekDay | null | undefined,
inp?: moment.MomentInput
): moment.Moment;Example:
import {
parseDate,
parseTimestamp,
getLocalTimeDisplay,
convertWeekDayToMomentWeekDay,
getMomentDateWithCustomStartOfWeek,
TimeFrames,
WeekDay,
} from '@lightdash/common';
// Parse date strings with different time intervals
const dailyDate = parseDate('2024-01-15', TimeFrames.DAY);
// Parses "2024-01-15" format -> Date object
const monthlyDate = parseDate('2024-01', TimeFrames.MONTH);
// Parses "2024-01" format -> Date object
const yearlyDate = parseDate('2024', TimeFrames.YEAR);
// Parses "YYYY" format -> Date object
const quarterDate = parseDate('2024-Q1', TimeFrames.QUARTER);
// Parses "YYYY-[Q]Q" format -> Date object
// Parse timestamp strings with time components
const timestampWithMinutes = parseTimestamp(
'2024-01-15, 10:30 (Z)',
TimeFrames.MINUTE
);
// Parses "YYYY-MM-DD, HH:mm (Z)" format -> Date object
const timestampWithSeconds = parseTimestamp(
'2024-01-15, 10:30:45 (Z)',
TimeFrames.SECOND
);
// Parses "YYYY-MM-DD, HH:mm:ss (Z)" format -> Date object
const timestampWithMillis = parseTimestamp(
'2024-01-15, 10:30:45:123 (Z)',
TimeFrames.MILLISECOND
);
// Parses "YYYY-MM-DD, HH:mm:ss:SSS (Z)" format -> Date object
// Display local time with timezone
const localTime = getLocalTimeDisplay(new Date());
// Returns: "2024-01-15 10:30 (America/New_York)"
const localTimeWithoutTz = getLocalTimeDisplay(new Date(), false);
// Returns: "2024-01-15 10:30 "
// Convert WeekDay to moment.js format
const mondayInMoment = convertWeekDayToMomentWeekDay(WeekDay.MONDAY);
// Returns: 1 (Monday in moment.js is 1, not 0)
const sundayInMoment = convertWeekDayToMomentWeekDay(WeekDay.SUNDAY);
// Returns: 0 (Sunday in moment.js is 0)
// Create moment with custom week start for accurate week operations
const dateStr = '2024-01-15';
// Default moment (week starts on Sunday)
const defaultMoment = getMomentDateWithCustomStartOfWeek(null, dateStr);
const weekStartDefault = defaultMoment.startOf('week');
// Week starts on Sunday
// Custom week start on Monday
const mondayMoment = getMomentDateWithCustomStartOfWeek(WeekDay.MONDAY, dateStr);
const weekStartMonday = mondayMoment.startOf('week');
// Week starts on Monday
// Custom week start on Wednesday
const wednesdayMoment = getMomentDateWithCustomStartOfWeek(WeekDay.WEDNESDAY, dateStr);
const weekStartWednesday = wednesdayMoment.startOf('week');
// Week starts on Wednesday
// Use for week-based calculations
const customWeekMoment = getMomentDateWithCustomStartOfWeek(WeekDay.MONDAY, '2024-06-15');
const weekStart = customWeekMoment.startOf('week'); // Gets Monday of that week
const weekEnd = customWeekMoment.endOf('week'); // Gets Sunday of that week
const isoWeek = customWeekMoment.isoWeek(); // Gets ISO week numberComplete signature for formatting field values with all options.
/**
* Format a value according to field configuration
* Main formatting function used throughout the application
* Handles custom formats, format expressions, dates, timestamps, and parameters
* @param item - Field, dimension, metric, table calculation, or custom dimension
* @param value - Value to format
* @param convertToUTC - Whether to convert timestamps to UTC (default: undefined)
* @param parameters - Optional parameters for template substitution in format expressions
* @returns Formatted string value
*/
function formatItemValue(
item: Field | Dimension | AdditionalMetric | TableCalculation | CustomDimension | undefined,
value: unknown,
convertToUTC?: boolean,
parameters?: Record<string, unknown>
): string;Example:
import { formatItemValue } from '@lightdash/common';
// Format with parameters for conditional formatting
const item = {
name: 'revenue',
type: DimensionType.NUMBER,
format: '${p.currency_format}', // Uses parameter
formatOptions: { type: CustomFormatType.CURRENCY, currency: 'USD' }
};
const formatted1 = formatItemValue(
item,
1234.56,
true,
{ 'p.currency_format': '"$"#,##0.00' }
);
// Returns: "$1,234.56"
// Format timestamp with UTC conversion
const timestampField = {
name: 'created_at',
type: DimensionType.TIMESTAMP,
timeInterval: TimeFrames.DAY,
};
const formatted2 = formatItemValue(
timestampField,
new Date('2024-01-15T10:30:00Z'),
true
);
// Returns: "2024-01-15"
// Format without field (uses default formatting)
const formatted3 = formatItemValue(undefined, 1234.56);
// Returns: "1234.56"Functions for working with time and week configuration.
function convertWeekDayToMomentWeekDay(weekDay: WeekDay): number;
function getMomentDateWithCustomStartOfWeek(
startOfWeek: WeekDay | null | undefined,
inp?: moment.MomentInput
): moment.Moment;Example:
import {
convertWeekDayToMomentWeekDay,
getMomentDateWithCustomStartOfWeek,
WeekDay
} from '@lightdash/common';
// Convert Lightdash WeekDay (0=Monday to 6=Sunday) to moment.js format (0=Sunday to 6=Saturday)
const momentDay = convertWeekDayToMomentWeekDay(WeekDay.MONDAY);
// Returns: 1
// Create moment with custom week start for accurate week calculations
const date = '2024-01-15';
const momentWithCustomWeek = getMomentDateWithCustomStartOfWeek(WeekDay.MONDAY, date);
// moment.Moment with Monday as first day of week
// Use for accurate startOf/endOf week operations
const weekStart = getMomentDateWithCustomStartOfWeek(WeekDay.SUNDAY, date).startOf('week');
const weekEnd = getMomentDateWithCustomStartOfWeek(WeekDay.SUNDAY, date).endOf('week');const timeFrameConfigs: Record<TimeFrames, TimeFrameConfig>;
interface TimeFrameConfig {
label: string;
format: string;
getStartOfPeriod(date: moment.Moment): moment.Moment;
getEndOfPeriod(date: moment.Moment): moment.Moment;
}Utilities for working with time frames and date granularity.
/**
* Enum for days of the week (0-6, where 0 = Monday)
*/
enum WeekDay {
MONDAY = 0,
TUESDAY = 1,
WEDNESDAY = 2,
THURSDAY = 3,
FRIDAY = 4,
SATURDAY = 5,
SUNDAY = 6,
}
/**
* Type guard to check if a value is a valid WeekDay
* @param value - Value to check
* @returns true if value is a WeekDay
*/
function isWeekDay(value: unknown): value is WeekDay;
/**
* Configuration for time frame operations
* Maps time frames to their SQL generation functions, labels, and axis formatting
*/
const timeFrameConfigs: Record<TimeFrames, TimeFrameConfig>;
/**
* Gets the default time frames for a dimension type
* @param type - The dimension type (DATE or TIMESTAMP)
* @returns Array of default time frames
*/
function getDefaultTimeFrames(type: DimensionType): TimeFrames[];
/**
* Type guard to check if a string is a valid TimeFrames value
* @param value - String to check
* @returns true if value is a valid TimeFrames
*/
function isTimeInterval(value: string): value is TimeFrames;
/**
* Validates and converts an array of strings to TimeFrames
* @param values - Array of strings to validate
* @returns Array of valid TimeFrames values
*/
function validateTimeFrames(values: string[]): TimeFrames[];
/**
* Sorts time frames in chronological order
* @param a - First time frame
* @param b - Second time frame
* @returns Sort comparison result
*/
function sortTimeFrames(a: TimeFrames, b: TimeFrames): number;
/**
* Extracts the base dimension ID and time frame from a dimension ID
* @param dimensionId - The dimension ID (e.g., "orders_created_day")
* @returns Object with baseDimensionId and newTimeFrame, or empty object if not a date dimension
*/
function getDateDimension(dimensionId: string): {
baseDimensionId?: string;
newTimeFrame?: TimeFrames;
};
/**
* Gets warehouse-specific SQL for truncating dates
* @param adapterType - The warehouse adapter type
* @param timeFrame - The time frame to truncate to
* @param originalSql - The original SQL expression
* @param type - The dimension type
* @param startOfWeek - Optional start of week day
* @returns SQL expression for date truncation
*/
function getSqlForTruncatedDate(
adapterType: SupportedDbtAdapter,
timeFrame: TimeFrames,
originalSql: string,
type: DimensionType,
startOfWeek?: WeekDay | null
): string;Example:
import {
WeekDay,
isWeekDay,
getDefaultTimeFrames,
validateTimeFrames,
sortTimeFrames,
getDateDimension,
} from '@lightdash/common';
// Get default time frames for a timestamp dimension
const timeFrames = getDefaultTimeFrames(DimensionType.TIMESTAMP);
// Returns: [RAW, DAY, WEEK, MONTH, QUARTER, YEAR]
// Validate time frame strings
const validated = validateTimeFrames(['day', 'WEEK', 'invalid']);
// Returns: [TimeFrames.DAY, TimeFrames.WEEK]
// Sort time frames chronologically
const sorted = [TimeFrames.YEAR, TimeFrames.DAY, TimeFrames.MONTH].sort(sortTimeFrames);
// Returns: [TimeFrames.DAY, TimeFrames.MONTH, TimeFrames.YEAR]
// Parse dimension with time frame
const parsed = getDateDimension('orders_created_day');
// Returns: { baseDimensionId: 'orders_created', newTimeFrame: TimeFrames.DAY }
// Check week day
if (isWeekDay(0)) {
console.log('Monday is a valid week day');
}Functions for formatting query result values and configuring chart axes.
/**
* Extracts result value arrays from result rows with optional min/max calculation.
* @param rows - Array of result rows
* @param preferRaw - Whether to prefer raw over formatted values
* @param calculateMinAndMax - Whether to calculate min/max for numeric values
* @param excludeNulls - Whether to exclude null values
* @returns Object with results array and optional min/max map
*/
function getResultValueArray(
rows: ResultRow[],
preferRaw?: boolean,
calculateMinAndMax?: boolean,
excludeNulls?: boolean
): {
results: Record<string, unknown>[];
minsAndMaxes?: Record<string, { min: number; max: number }>;
};
/**
* Gets display label for date-grouped dimensions, removing timeframe suffixes.
* @param axisItem - Item from items map
* @returns Cleaned label without timeframe (e.g., "Order created" instead of "Order created day")
*/
function getDateGroupLabel(axisItem: ItemsMap[string]): string | undefined;
/**
* Gets axis name for chart configuration based on series and axis index.
* @param isAxisTheSameForAllSeries - Whether all series share the same axis
* @param selectedAxisIndex - Currently selected axis index
* @param axisReference - Axis reference ('xRef' or 'yRef')
* @param axisIndex - Current axis index
* @param axisName - Optional explicit axis name
* @param series - Array of chart series
* @param itemsMap - Map of items for field lookup
* @returns Axis name or undefined
*/
function getAxisName(args: {
isAxisTheSameForAllSeries: boolean;
selectedAxisIndex: number;
axisReference: 'yRef' | 'xRef';
axisIndex: number;
axisName?: string;
series?: Series[];
itemsMap: ItemsMap | undefined;
}): string | undefined;
/**
* Formats a single row from raw database values to result row format.
* @param row - Raw row data from database
* @param itemsMap - Map of items for field lookup
* @param pivotValuesColumns - Optional pivot column mapping
* @param parameters - Optional parameter values for templating
* @returns Result row with formatted and raw values
*/
function formatRow(
row: { [col: string]: AnyType },
itemsMap: ItemsMap,
pivotValuesColumns?: Record<string, PivotValuesColumn> | null,
parameters?: Record<string, unknown>
): ResultRow;
/**
* Formats multiple rows from raw database values to result row format.
* @param rows - Array of raw rows from database
* @param itemsMap - Map of items for field lookup
* @param pivotValuesColumns - Optional pivot column mapping
* @param parameters - Optional parameter values for templating
* @returns Array of result rows with formatted and raw values
*/
function formatRows(
rows: { [col: string]: AnyType }[],
itemsMap: ItemsMap,
pivotValuesColumns?: Record<string, PivotValuesColumn> | null,
parameters?: Record<string, unknown>
): ResultRow[];
/**
* Formats raw value for a field (handles date/timestamp UTC conversion).
* @param field - Field or metric definition
* @param value - Raw value from database
* @returns Formatted raw value (dates converted to UTC)
*/
function formatRawValue(
field: Field | Metric | TableCalculation | CustomDimension | undefined,
value: AnyType
): AnyType;
/**
* Formats multiple raw rows for comparison (dates to UTC).
* @param rows - Array of raw rows
* @param itemsMap - Map of items for field lookup
* @returns Array of rows with formatted raw values
*/
function formatRawRows(
rows: { [col: string]: AnyType }[],
itemsMap: ItemsMap
): Record<string, unknown>[];
/**
* Gets field IDs from a metric query.
* @param metricQuery - The metric query
* @returns Array of field IDs (metrics + dimensions + table calculations)
*/
function itemsInMetricQuery(metricQuery: MetricQuery | undefined): string[];Example:
import {
getResultValueArray,
getDateGroupLabel,
getAxisName,
formatRow,
formatRows,
itemsInMetricQuery
} from '@lightdash/common';
// Extract values with min/max calculation
const { results, minsAndMaxes } = getResultValueArray(
rows,
true, // prefer raw values
true, // calculate min/max
false // don't exclude nulls
);
// Use min/max for conditional formatting
if (minsAndMaxes?.revenue) {
console.log(`Revenue range: ${minsAndMaxes.revenue.min} - ${minsAndMaxes.revenue.max}`);
}
// Get clean axis labels
const axisItem = itemsMap['orders_created_day'];
const label = getDateGroupLabel(axisItem);
// Returns: "Orders created" (without "day" suffix)
// Configure axis names
const axisName = getAxisName({
isAxisTheSameForAllSeries: false,
selectedAxisIndex: 0,
axisReference: 'yRef',
axisIndex: 0,
axisName: 'Revenue',
series: chartSeries,
itemsMap
});
// Format database rows
const formattedRows = formatRows(rawRows, itemsMap, pivotColumns, parameters);
// Get field IDs from query
const fieldIds = itemsInMetricQuery(metricQuery);
// Returns: ['customers_customer_id', 'orders_total', 'calc_profit']Utilities for internationalization of chart-as-code and dashboard-as-code content, enabling multi-language support for analytics configurations.
/**
* Base abstract class for internationalizing as-code content
* Provides common patterns for chart and dashboard internationalization
*/
abstract class AsCodeInternalization<
T extends { type: 'chart' | 'dashboard'; content: ChartAsCode | DashboardAsCode },
Z extends z.AnyZodObject
> {
protected abstract schema: Z;
/**
* Extracts translatable content from as-code configuration
* @param asCode - The chart or dashboard as-code configuration
* @returns Language map with translatable fields organized by slug
*/
public abstract getLanguageMap(asCode: T['content']): {
[typeKey in T['type']]: {
[slugKey in T['content']['slug']]: PartialDeep<
T['content'],
{ recurseIntoArrays: true }
>;
};
};
/**
* Merges translations back into the as-code content
* @param internalizationMap - Partial translations to merge
* @param content - Original as-code content
* @returns Merged content with translations applied
*/
public abstract merge(
internalizationMap: ReturnType<this['getLanguageMap']>[T['type']][string],
content: T['content']
): T['content'];
}
/**
* Internationalization handler for chart-as-code configurations
* Supports all chart types: cartesian, pie, funnel, big number, table, custom
*/
class ChartAsCodeInternalization extends AsCodeInternalization<
{ type: 'chart'; content: ChartAsCode },
typeof chartAsCodeSchema
> {
constructor(protected schema?: typeof chartAsCodeSchema);
/**
* Extracts translatable fields from chart configuration
* Includes name, description, axis labels, series names, mark lines, etc.
* @param chartAsCode - The chart as-code configuration
* @returns Language map with chart slug as key
*/
public getLanguageMap(chartAsCode: ChartAsCode): ChartAsCodeLanguageMap;
/**
* Merges translations into chart configuration
* @param internalizationMap - Translations to apply
* @param content - Original chart configuration
* @returns Chart with translations merged
*/
public merge(
internalizationMap: ChartAsCodeLanguageMap['chart'][string],
content: ChartAsCode
): ChartAsCode;
}
/**
* Internationalization handler for dashboard-as-code configurations
* Handles dashboard metadata and tile properties
*/
class DashboardAsCodeInternalization extends AsCodeInternalization<
{ type: 'dashboard'; content: DashboardAsCode },
typeof dashboardAsCodeSchema
> {
constructor(protected schema?: typeof dashboardAsCodeSchema);
/**
* Extracts translatable fields from dashboard configuration
* Includes name, description, tile titles, and markdown content
* @param dashboardAsCode - The dashboard as-code configuration
* @returns Language map with dashboard slug as key
*/
public getLanguageMap(dashboardAsCode: DashboardAsCode): DashboardAsCodeLanguageMap;
/**
* Merges translations into dashboard configuration
* @param internalizationMap - Translations to apply
* @param content - Original dashboard configuration
* @returns Dashboard with translations merged
*/
public merge(
internalizationMap: DashboardAsCodeLanguageMap['dashboard'][string],
content: DashboardAsCode
): DashboardAsCode;
}
/**
* Merges existing translations into content, only replacing fields present in right
* Performs deep recursive merge while preserving structure
* @param left - Original content (complete structure)
* @param right - Translations to merge (partial structure)
* @returns Merged content with translations applied
*/
function mergeExisting(left: any, right: any): any;
// Type exports
type ChartAsCodeLanguageMap = ReturnType<ChartAsCodeInternalization['getLanguageMap']>;
type DashboardAsCodeLanguageMap = ReturnType<DashboardAsCodeInternalization['getLanguageMap']>;
type LanguageMap = Partial<ChartAsCodeLanguageMap & DashboardAsCodeLanguageMap>;Usage Examples:
import {
ChartAsCodeInternalization,
DashboardAsCodeInternalization,
mergeExisting,
type ChartAsCode,
type DashboardAsCode,
type LanguageMap,
} from '@lightdash/common';
// Extract translatable content from a chart
const chartI18n = new ChartAsCodeInternalization();
const chartAsCode: ChartAsCode = {
slug: 'monthly-revenue',
name: 'Monthly Revenue',
description: 'Revenue breakdown by month',
chartConfig: {
type: 'cartesian',
config: {
eChartsConfig: {
xAxis: [{ name: 'Month' }],
yAxis: [{ name: 'Revenue (USD)' }],
series: [
{
name: 'Total Revenue',
markLine: {
data: [{ name: 'Target' }],
},
},
],
},
},
},
};
// Extract language map with translatable fields
const languageMap = chartI18n.getLanguageMap(chartAsCode);
console.log(languageMap);
// {
// chart: {
// 'monthly-revenue': {
// name: 'Monthly Revenue',
// description: 'Revenue breakdown by month',
// chartConfig: {
// config: {
// eChartsConfig: {
// xAxis: [{ name: 'Month' }],
// yAxis: [{ name: 'Revenue (USD)' }],
// series: [{ name: 'Total Revenue', markLine: { data: [{ name: 'Target' }] } }]
// }
// }
// }
// }
// }
// }
// Apply Spanish translations
const spanishTranslations = {
name: 'Ingresos Mensuales',
description: 'Desglose de ingresos por mes',
chartConfig: {
config: {
eChartsConfig: {
xAxis: [{ name: 'Mes' }],
yAxis: [{ name: 'Ingresos (USD)' }],
series: [
{
name: 'Ingresos Totales',
markLine: { data: [{ name: 'Objetivo' }] },
},
],
},
},
},
};
const translatedChart = chartI18n.merge(spanishTranslations, chartAsCode);
// Extract translatable content from a dashboard
const dashboardI18n = new DashboardAsCodeInternalization();
const dashboardAsCode: DashboardAsCode = {
slug: 'sales-overview',
name: 'Sales Overview',
description: 'Key sales metrics',
tiles: [
{
type: 'saved_chart',
properties: {
title: 'Revenue Chart',
chartName: 'monthly-revenue',
},
},
{
type: 'markdown',
properties: {
title: 'Analysis Notes',
content: '## Key Insights\nRevenue is trending upward',
},
},
],
};
const dashboardLanguageMap = dashboardI18n.getLanguageMap(dashboardAsCode);
// Apply translations
const frenchTranslations = {
name: 'Aperçu des Ventes',
description: 'Métriques clés des ventes',
tiles: [
{
properties: {
title: 'Graphique des Revenus',
},
},
{
properties: {
title: 'Notes d\'Analyse',
content: '## Informations Clés\nLes revenus sont en hausse',
},
},
],
};
const translatedDashboard = dashboardI18n.merge(frenchTranslations, dashboardAsCode);
// Using mergeExisting directly for custom merge scenarios
const original = {
title: 'Original Title',
config: { color: 'blue', size: 10 },
metadata: { created: '2024-01-01' },
};
const updates = {
title: 'Updated Title',
config: { color: 'red' }, // size preserved
};
const merged = mergeExisting(original, updates);
// Result: { title: 'Updated Title', config: { color: 'red', size: 10 }, metadata: { created: '2024-01-01' } }Functions for formatting raw database rows into structured result rows with both raw and formatted values.
/**
* Formats a single raw value based on field type (handles dates/timestamps)
* @param field - Field definition (dimension, metric, table calculation, or custom dimension)
* @param value - Raw value from the database
* @returns Formatted value (dates/timestamps converted to UTC strings)
*/
function formatRawValue(
field: Field | Metric | TableCalculation | CustomDimension | undefined,
value: AnyType
): AnyType;
/**
* Formats an array of raw database rows (handles dates/timestamps only)
* @param rows - Array of raw database rows
* @param itemsMap - Map of field IDs to field definitions
* @returns Array of rows with raw values formatted for dates/timestamps
*/
function formatRawRows(
rows: { [col: string]: AnyType }[],
itemsMap: ItemsMap
): Record<string, unknown>[];
/**
* Formats a single database row into a structured result row
* @param row - Raw database row
* @param itemsMap - Map of field IDs to field definitions
* @param pivotValuesColumns - Optional pivot configuration
* @param parameters - Optional parameter values for formatting
* @returns Result row with both raw and formatted values
*/
function formatRow(
row: { [col: string]: AnyType },
itemsMap: ItemsMap,
pivotValuesColumns?: Record<string, PivotValuesColumn> | null,
parameters?: Record<string, unknown>
): ResultRow;
/**
* Formats an array of database rows into structured result rows
* @param rows - Array of raw database rows
* @param itemsMap - Map of field IDs to field definitions
* @param pivotValuesColumns - Optional pivot configuration
* @param parameters - Optional parameter values for formatting
* @returns Array of result rows with both raw and formatted values
*/
function formatRows(
rows: { [col: string]: AnyType }[],
itemsMap: ItemsMap,
pivotValuesColumns?: Record<string, PivotValuesColumn> | null,
parameters?: Record<string, unknown>
): ResultRow[];Example:
import {
formatRawValue,
formatRawRows,
formatRow,
formatRows,
getItemMap,
} from '@lightdash/common';
// Format a single raw value
const dateField = explore.tables.orders.dimensions.created_at;
const rawDate = new Date('2024-01-15T10:30:00Z');
const formattedDate = formatRawValue(dateField, rawDate);
// Returns: "2024-01-15T10:30:00.000Z" (UTC string)
// Format raw rows (dates/timestamps only)
const rawRows = [
{ customers_name: 'Alice', orders_created_at: new Date('2024-01-15') },
{ customers_name: 'Bob', orders_created_at: new Date('2024-01-16') },
];
const itemsMap = getItemMap(explore);
const formattedRawRows = formatRawRows(rawRows, itemsMap);
// Returns: [
// { customers_name: 'Alice', orders_created_at: '2024-01-15T00:00:00.000Z' },
// { customers_name: 'Bob', orders_created_at: '2024-01-16T00:00:00.000Z' },
// ]
// Format a complete result row with both raw and formatted values
const singleRow = {
customers_name: 'Charlie',
customers_total_revenue: 15234.56,
orders_created_at: new Date('2024-01-17'),
};
const resultRow = formatRow(singleRow, itemsMap);
// Returns: {
// customers_name: {
// value: { raw: 'Charlie', formatted: 'Charlie' }
// },
// customers_total_revenue: {
// value: { raw: 15234.56, formatted: '$15,234.56' }
// },
// orders_created_at: {
// value: { raw: '2024-01-17T00:00:00.000Z', formatted: '2024-01-17' }
// }
// }
// Format multiple rows
const multipleRawRows = [
{ product_name: 'Widget', revenue: 1234.5 },
{ product_name: 'Gadget', revenue: 5678.9 },
];
const resultRows = formatRows(multipleRawRows, itemsMap);
// Each row will have both raw and formatted valuesKey differences:
formatRawValue() - Handles single values, primarily for date/timestamp UTC conversionformatRawRows() - Processes arrays of rows but only formats dates/timestampsformatRow() - Full formatting with both raw and formatted values in ResultRow structureformatRows() - Batch version of formatRow() for multiple rows