or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

authorization.mdcharts.mdcompiler.mdconditional-formatting.mddashboards.mddbt.mdee-features.mdexplore-fields.mdfilters.mdformatting.mdindex.mdmetric-queries.mdparameters.mdpivot.mdprojects-spaces.mdsql-runner.mdtemplating.mdtypes.mdutilities.mdvisualizations.mdwarehouse.md
tile.json

utilities.mddocs/

Utility Functions

Comprehensive utilities for validation, transformation, formatting, and data manipulation.

Quick Reference

CategoryFunctionsPurpose
ValidationvalidateEmail, validatePassword, getEmailSchemaEmail/password validation with Zod schemas
StringsnakeCaseName, friendlyName, capitalizeCase conversion and name transformation
ArrayhasIntersection, toggleArrayValue, replaceStringInArrayArray operations and comparisons
ObjectremoveEmptyProperties, deepEqualObject manipulation and comparison
ColorhexToRGB, isHexCodeColor, getReadableTextColorColor conversion and validation
Type GuardsisNotNull, isLightdashMode, isWeekDayRuntime type checking
AccessorsgetArrayValue, getObjectValue, hasPropertySafe property access with error handling
TimeconvertWeekDayToMomentWeekDay, getMomentDateWithCustomStartOfWeekWeek configuration and time utilities
FormattingformatItemValue, formatRows, formatDateValue formatting for display
Query ResultsgetResultValueArray, formatRawValue, itemsInMetricQueryResult transformation
ChartsgetDateGroupLabel, getAxisName, sortedItemsForXAxisChart configuration helpers
ItemsgetItemId, getItemLabel, isNumericItem, isDateItemItem identification and type checking
SlugsgenerateSlug, getLtreePathFromSlug, getDeepestPathsURL-safe identifiers
FiltersgetFilterRulesFromGroup, getTotalFilterRules, countTotalFilterRulesFilter extraction
Time FramesgetDefaultTimeFrames, validateTimeFrames, sortTimeFramesTime interval utilities
PromisesisFulfilled, isRejected, getFulfilledValuesPromise.allSettled helpers
SchedulergetHumanReadableCronExpression, isValidFrequencyCron and scheduler utilities
i18nChartAsCodeInternalization, DashboardAsCodeInternalizationMulti-language support
DependenciesdetectCircularDependenciesGraph cycle detection
Metrics ExplorergetFieldIdForDateDimension, getMetricExplorerDataPointsMetrics explorer helpers

Validation

Email Validation

/**
 * Validates an email address using a regular expression
 * Checks for valid email format and rejects whitespace
 * @param email - The email address to validate
 * @returns true if the email is valid, false otherwise
 */
function validateEmail(email: string): boolean;

/**
 * Returns a Zod schema for email validation
 * @returns Zod schema that validates email format and rejects whitespace
 */
function getEmailSchema(): ZodSchema;

/**
 * Validates a full email address using a stricter regex pattern
 * @param email - The email address to validate
 * @returns true if the email address matches the pattern
 */
function isValidEmailAddress(email: string): boolean;

/**
 * Extracts the domain portion from an email address
 * @param email - The email address
 * @returns The domain in lowercase (part after @)
 * @throws Error if email contains whitespace or is invalid
 */
function getEmailDomain(email: string): string;

/**
 * Validates that a domain string has valid format for email domains
 * Checks against VALID_EMAIL_DOMAIN_REGEX pattern
 * @param value - The domain to validate
 * @returns true if the domain format is valid
 */
function isValidEmailDomain(value: string): boolean;

/**
 * Validates organization email domains, rejecting common email providers
 * Ensures domains are not consumer email services (gmail.com, yahoo.com, etc.)
 * @param domains - Array of domain strings to validate
 * @returns Error message string if invalid domains found, undefined if all valid
 */
function validateOrganizationEmailDomains(domains: string[]): string | undefined;

Example:

import {
  validateEmail,
  getEmailSchema,
  isValidEmailAddress,
  getEmailDomain,
  isValidEmailDomain,
  validateOrganizationEmailDomains,
} from '@lightdash/common';

// Basic email validation
if (validateEmail('user@example.com')) {
  console.log('Valid email');
}

// Use with Zod for form validation
const schema = z.object({
  email: getEmailSchema(),
});

// Stricter validation
if (isValidEmailAddress('user@company.example.com')) {
  console.log('Valid email address');
}

// Extract domain from email
const domain = getEmailDomain('user@company.com'); // Returns 'company.com'

// Validate domain format
if (isValidEmailDomain('company.com')) {
  console.log('Valid domain format');
}

// Validate organization domains (rejects consumer email providers)
const error = validateOrganizationEmailDomains(['company.com', 'gmail.com']);
if (error) {
  console.error(error); // "gmail.com is not allowed as organization email"
}

// Valid organization domains
const valid = validateOrganizationEmailDomains(['company.com', 'business.org']);
console.log(valid); // undefined (no errors)

Password Validation

function validatePassword(password: string): boolean;
function getPasswordSchema(): ZodSchema;

Example:

import { validatePassword, getPasswordSchema } from '@lightdash/common';

if (validatePassword('SecurePass123!')) {
  console.log('Valid password');
}

// Use with Zod
const schema = z.object({
  password: getPasswordSchema(),
});

Organization and User Validation

function getOrganizationNameSchema(): ZodSchema;

const CompleteUserSchema: z.ZodObject;
type CompleteUserArgs = z.infer<typeof CompleteUserSchema>;

String Utilities

function snakeCaseName(text: string): string;
function friendlyName(text: string): string;
function capitalize(text: string): string;
function hasSpecialCharacters(text: string): boolean;

Examples:

import { snakeCaseName, friendlyName, capitalize, hasSpecialCharacters } from '@lightdash/common';

snakeCaseName('Customer Name');  // "customer_name"
friendlyName('customer_id');     // "Customer id"
capitalize('hello world');       // "Hello world"
hasSpecialCharacters('test@123'); // true

Array Utilities

function hasIntersection(tags: string[], tags2: string[]): boolean;
function toggleArrayValue<T>(arr: T[], value: T): T[];
function replaceStringInArray(arr: string[], oldVal: string, newVal: string): string[];

Examples:

import { hasIntersection, toggleArrayValue, replaceStringInArray } from '@lightdash/common';

// Check if arrays have common elements
if (hasIntersection(['tag1', 'tag2'], ['tag2', 'tag3'])) {
  console.log('Arrays intersect');
}

// Toggle value in array
const tags = ['tag1', 'tag2'];
const newTags = toggleArrayValue(tags, 'tag2');
// Returns: ['tag1'] (removed 'tag2')

const withTag = toggleArrayValue(newTags, 'tag3');
// Returns: ['tag1', 'tag3'] (added 'tag3')

// Replace string in array
const dimensions = ['customers.id', 'orders.id', 'customers.name'];
const updated = replaceStringInArray(dimensions, 'customers.id', 'users.id');
// Returns: ['users.id', 'orders.id', 'customers.name']

Object Utilities

function removeEmptyProperties(obj: Record<string, unknown>): Record<string, unknown>;
function deepEqual(obj1: Record<string, unknown>, obj2: Record<string, unknown>): boolean;

Examples:

import { removeEmptyProperties, deepEqual } from '@lightdash/common';

const cleaned = removeEmptyProperties({
  name: 'test',
  value: undefined,
  count: null,
  enabled: false,
});
// Returns: { name: 'test', enabled: false }

const isEqual = deepEqual(
  { a: 1, b: { c: 2 } },
  { a: 1, b: { c: 2 } }
);
// Returns: true

Color Utilities

function hexToRGB(hex: string, alpha?: number): string;
function isHexCodeColor(color: string): boolean;
function getInvalidHexColors(colors: string[]): string[];
function cleanColorArray(colors: string[]): string[];
function getReadableTextColor(backgroundColor: string): string;

Examples:

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', '#123']

// Clean color array (removes invalid colors)
const cleaned = cleanColorArray(['#FF5733', 'invalid', '#4C8BF5']);
// Returns: ['#FF5733', '#4C8BF5']

// Get readable text color (white or black) for background
const textColor = getReadableTextColor('#000000');
// Returns: "white"

Type Guards

function isNotNull<T>(arg: T): arg is Exclude<T, null>;
function isLightdashMode(x: string): x is LightdashMode;
function isWeekDay(value: unknown): value is WeekDay;
function isFormat(value: string | undefined): value is Format;
function isTimeInterval(value: string): value is TimeFrames;

Examples

const values: (string | null)[] = ['a', null, 'b'];
const nonNull = values.filter(isNotNull);  // string[]

Accessor Functions

Safe property access with error handling.

function getArrayValue<T>(obj: ArrayLike<T> | undefined, key: number, errorMessage?: string): T;
function getObjectValue<T>(obj: Record<string | number, T> | undefined, key: string | number, errorMessage?: string): T;
function hasProperty<T>(obj: unknown, key: string): obj is Record<string, T>;

Examples

// Throws UnexpectedIndexError if not found
const item = getArrayValue(items, 1);
const value = getObjectValue(config, 'key');

Assertions

function assertUnreachable(value: never, message?: string): never;

Example

type Status = 'pending' | 'success' | 'error';

function handleStatus(status: Status) {
  switch (status) {
    case 'pending': return 'Processing...';
    case 'success': return 'Done!';
    case 'error': return 'Failed';
    default: return assertUnreachable(status);  // TypeScript compile error if cases missing
  }
}

Time and Week Utilities

enum WeekDay { MONDAY = 0, TUESDAY = 1, WEDNESDAY = 2, THURSDAY = 3, FRIDAY = 4, SATURDAY = 5, SUNDAY = 6 }

function convertWeekDayToMomentWeekDay(weekDay: WeekDay): number;
function getMomentDateWithCustomStartOfWeek(startOfWeek: WeekDay | null | undefined, inp?: moment.MomentInput): moment.Moment;

// Time frame utilities
const timeFrameConfigs: Record<TimeFrames, TimeFrameConfig>;
function getDefaultTimeFrames(type: DimensionType): TimeFrames[];
function validateTimeFrames(values: string[]): TimeFrames[];
function sortTimeFrames(a: TimeFrames, b: TimeFrames): number;
function getDateDimension(dimensionId: string): { baseDimensionId?: string; newTimeFrame?: TimeFrames };
function getSqlForTruncatedDate(
  adapterType: SupportedDbtAdapter,
  timeFrame: TimeFrames,
  originalSql: string,
  type: DimensionType,
  startOfWeek?: WeekDay | null
): string;

Value Formatting

function formatItemValue(item: Field | TableCalculation | undefined, value: unknown, convertToUTC?: boolean): string;
function formatDate(date: Date | string, timeInterval?: TimeFrames, convertToUTC?: boolean): string;
function formatTimestamp(value: Date | string, timeInterval?: TimeFrames, convertToUTC?: boolean): string;
function formatNumberValue(value: number, options?: NumberFormatOptions): string;
function applyCustomFormat(item: Field | Metric, value: unknown, customFormat: CustomFormat): string;

Query Result Utilities

function getResultValueArray(
  rows: ResultRow[],
  preferRaw?: boolean,
  calculateMinAndMax?: boolean,
  excludeNulls?: boolean
): { results: Record<string, unknown>[]; minsAndMaxes?: Record<string, { min: number; max: number }> };

function formatRawValue(field: Field | undefined, value: unknown): unknown;
function formatRawRows(rows: { [col: string]: unknown }[], itemsMap: ItemsMap): Record<string, unknown>[];

function formatRow(
  row: { [col: string]: unknown },
  itemsMap: ItemsMap,
  pivotValuesColumns?: Record<string, PivotValuesColumn> | null,
  parameters?: Record<string, unknown>
): ResultRow;

function formatRows(
  rows: { [col: string]: unknown }[],
  itemsMap: ItemsMap,
  pivotValuesColumns?: Record<string, PivotValuesColumn> | null,
  parameters?: Record<string, unknown>
): ResultRow[];

function itemsInMetricQuery(metricQuery?: MetricQuery): string[];

Examples

// Extract values with min/max calculation
const { results, minsAndMaxes } = getResultValueArray(rows, true, true, false);

// Format database rows
const formatted = formatRows(rawRows, itemsMap, pivotColumns, parameters);

// Get field IDs from query
const fieldIds = itemsInMetricQuery(metricQuery);
// Returns: ['customers_id', 'orders_total', 'calc_profit']

Chart Utilities

function getDateGroupLabel(axisItem: ItemsMap[string]): string | undefined;

function getAxisName(args: {
  isAxisTheSameForAllSeries: boolean;
  selectedAxisIndex: number;
  axisReference: 'yRef' | 'xRef';
  axisIndex: number;
  axisName?: string;
  series?: Series[];
  itemsMap: ItemsMap | undefined;
}): string | undefined;

function getDefaultChartConfig(chartKind: ChartKind): ChartConfig;
function isCompleteLayout(layout: Partial<CartesianChartLayout>): boolean;
function isCompleteEchartsConfig(config: Partial<EChartsConfig>): boolean;
function getCustomLabelsFromVizTableConfig(config: VizTableConfig | undefined): Record<string, string>;
function getHiddenFieldsFromVizTableConfig(config: VizTableConfig | undefined): string[];
function getColumnOrderFromVizTableConfig(config: VizTableConfig | undefined): string[];

// Dashboard utilities
function hasChartsInDashboard(dashboard: Dashboard): boolean;
function getDefaultChartTileSize(chartKind: ChartKind): { h: number; w: number };

Item Utilities

// Identification
function getItemId(item: Item): string;
function getItemLabel(item: Item): string;
function getItemLabelWithoutTableName(item: Item): string;
function getItemType(item: Item): DimensionType | MetricType;

// Type checking
function isNumericItem(item: Item | undefined): boolean;
function isStringDimension(item: Item | undefined): boolean;
function isDateItem(item: Item | undefined): boolean;

// UI helpers
function getItemIcon(item: Item): 'tag' | 'numerical' | 'function';
function getItemColor(item: Item): string;

// Analysis
function isSummable(item: Item | undefined): boolean;

// Chart axis sorting
function sortedItemsForXAxis(itemsMap: ItemsMap | undefined): ItemsMap[string][];
function sortedItemsForYAxis(itemsMap: ItemsMap | undefined): ItemsMap[string][];

Examples

const itemId = getItemId(item);                     // "customers_revenue"
const label = getItemLabel(item);                   // "Customers Revenue"
const shortLabel = getItemLabelWithoutTableName(item);  // "Revenue"

if (isNumericItem(item)) {
  // Can apply numeric formatting
}
if (isDateItem(item)) {
  // Can use date filters
}

const xAxisItems = sortedItemsForXAxis(itemsMap);
// Prioritizes: date dimensions > dimensions > custom dimensions > metrics

Custom Metric Utilities

function convertAdditionalMetric(additionalMetric: AdditionalMetric, table: Table): CompiledMetric;
function getCustomMetricType(type: DimensionType): MetricType[];
function canApplyFormattingToCustomMetric(item: Dimension, customMetricType: MetricType): boolean;

Examples

// Get available metrics for a number dimension
const numberMetrics = getCustomMetricType(DimensionType.NUMBER);
// Returns: [MIN, MAX, SUM, PERCENTILE, MEDIAN, AVERAGE, COUNT_DISTINCT, COUNT]

// Get available metrics for a string dimension
const stringMetrics = getCustomMetricType(DimensionType.STRING);
// Returns: [COUNT_DISTINCT, COUNT, MIN, MAX]

Slug Utilities

function generateSlug(name: string): string;
function getLtreePathFromSlug(slug: string): string;
function getLtreePathFromContentAsCodePath(path: string): string;
function getContentAsCodePathFromLtreePath(path: string): string;
function getDeepestPaths<T extends { path: string }>(items: T[]): T[];
function isSubPath(parentPath: string, childPath: string): boolean;

Examples

generateSlug('My Dashboard Name!');               // "my-dashboard-name"
getLtreePathFromSlug('parent/child/item');        // "parent.child.item"
isSubPath('a.b', 'a.b.c.d');                      // true

HTML and Sleep Utilities

function sanitizeHtml(html: string): string;
function sleep(ms: number): Promise<void>;

Promise Utilities

function isFulfilled<T>(input: PromiseSettledResult<T>): input is PromiseFulfilledResult<T>;
function isRejected(input: PromiseSettledResult<unknown>): input is PromiseRejectedResult;
function getFulfilledValues<T>(results: PromiseSettledResult<T>[]): T[];

Examples

const results = await Promise.allSettled([fetchUser(1), fetchUser(2), fetchUser(3)]);

const successful = results.filter(isFulfilled);
const failed = results.filter(isRejected);
const values = getFulfilledValues(results);

Scheduler and Cron Utilities

function getTzMinutesOffset(oldTz: string, newTz: string): number;
function formatMinutesOffset(offsetMins: number): string;
function getTimezoneLabel(timezone: string | undefined): string | undefined;
function getHumanReadableCronExpression(cronExpression: string, timezone: string): string;
function isValidFrequency(cronExpression: string): boolean;
function isValidTimezone(timezone: string | undefined): boolean;

Examples

getHumanReadableCronExpression('0 9 * * *', 'America/New_York');
// Returns: "at 09:00 AM (UTC -05:00)"

getTimezoneLabel('America/Los_Angeles');
// Returns: "(UTC -08:00) America/Los Angeles"

isValidFrequency('0 * * * *');  // true - runs hourly

Custom Dimension SQL Generation

function getFixedWidthBinSelectSql(args: {
  binWidth: number;
  baseDimensionSql: string;
  warehouseSqlBuilder: WarehouseSqlBuilder;
}): string;

function getCustomRangeSelectSql(args: {
  binRanges: BinRange[];
  baseDimensionSql: string;
  warehouseSqlBuilder: WarehouseSqlBuilder;
}): string;

Examples

// Fixed width bins (age groups: 0-9, 10-19, 20-29, etc.)
const ageGroupSql = getFixedWidthBinSelectSql({
  binWidth: 10,
  baseDimensionSql: 'customers.age',
  warehouseSqlBuilder
});

// Custom ranges (revenue tiers)
const revenueTierSql = getCustomRangeSelectSql({
  binRanges: [
    { to: 1000 },               // <1000
    { from: 1000, to: 5000 },   // 1000-5000
    { from: 5000, to: 10000 },  // 5000-10000
    { from: 10000 }             // ≥10000
  ],
  baseDimensionSql: 'orders.revenue',
  warehouseSqlBuilder
});

Metrics Explorer Utilities

function assertUnimplementedTimeframe(timeframe: TimeFrames): never;
function getFieldIdForDateDimension(fieldId: string, timeframe: TimeFrames): string;

function getDateCalcUtils(
  timeFrame: TimeFrames,
  grain?: TimeFrames
): { forward: (date: Date) => Date; back: (date: Date) => Date };

function getDateRangeFromString(dateRange: [string, string], timeFrame: TimeFrames): [Date, Date];
function getGrainForDateRange(dateRange: MetricExplorerDateRange): TimeFrames;

function getMetricsExplorerSegmentFilters(segmentDimension: CompiledDimension | undefined, values: string[]): FilterRule[];
function getMetricExplorerDateRangeFilters(
  timeDimensionBaseField: string | undefined,
  dateRange: MetricExplorerDateRange,
  timeInterval: TimeFrames
): FilterRule[];

function parseMetricValue(value: unknown): number | null;

function getMetricExplorerDataPoints(rows: ResultRow[], query: MetricExplorerQuery): MetricExploreDataPoint[];
function getMetricExplorerDataPointsWithCompare(rows: ResultRow[], query: MetricExplorerQuery): MetricExploreDataPoint[];

function getDefaultDateRangeFromInterval(interval: TimeFrames): MetricExplorerDateRange;
function getDefaultMetricTreeNodeDateRange(interval: TimeFrames): MetricExplorerDateRange;

function getFirstAvailableTimeDimension(tables: Record<string, CompiledTable>): DefaultTimeDimension | undefined;
function getDefaultTimeDimension(tables: Record<string, CompiledTable>): DefaultTimeDimension | undefined;
function getAvailableTimeDimensionsFromTables(tables: Record<string, CompiledTable>): CompiledDimension[];
function getAvailableSegmentDimensions(tables: Record<string, CompiledTable>): CompiledDimension[];
function getAvailableCompareMetrics(
  tables: Record<string, CompiledTable>,
  currentMetric?: MetricWithAssociatedTimeDimension
): CompiledMetric[];

Examples

// Get time-granular field ID
const monthlyFieldId = getFieldIdForDateDimension('orders.created_at', TimeFrames.MONTH);
// Returns: 'orders.created_at_month'

// Create date range filters
const filters = getMetricExplorerDateRangeFilters(
  'orders.created_at',
  { from: new Date('2024-01-01'), to: new Date('2024-12-31') },
  TimeFrames.MONTH
);

// Parse metric values safely
const value = parseMetricValue('123.45');  // 123.45
const invalid = parseMetricValue('invalid');  // null

Dependency Graph Utilities

interface DependencyNode { name: string; dependencies: string[] }

function detectCircularDependencies(dependencies: DependencyNode[], errorPrefix?: string): void;

Example

const dependencies = [
  { name: 'A', dependencies: ['B'] },
  { name: 'B', dependencies: ['C'] },
  { name: 'C', dependencies: ['A'] }
];

detectCircularDependencies(dependencies, 'table calculations');
// Throws: "Circular dependency detected in table calculations: A -> B -> C -> A"

Internationalization (i18n)

class ChartAsCodeInternalization {
  constructor(schema?: typeof chartAsCodeSchema);
  getLanguageMap(chartAsCode: ChartAsCode): ChartAsCodeLanguageMap;
  merge(internalizationMap: ChartAsCodeLanguageMap['chart'][string], content: ChartAsCode): ChartAsCode;
}

class DashboardAsCodeInternalization {
  constructor(schema?: typeof dashboardAsCodeSchema);
  getLanguageMap(dashboardAsCode: DashboardAsCode): DashboardAsCodeLanguageMap;
  merge(internalizationMap: DashboardAsCodeLanguageMap['dashboard'][string], content: DashboardAsCode): DashboardAsCode;
}

function mergeExisting(left: any, right: any): any;

type ChartAsCodeLanguageMap = ReturnType<ChartAsCodeInternalization['getLanguageMap']>;
type DashboardAsCodeLanguageMap = ReturnType<DashboardAsCodeInternalization['getLanguageMap']>;
type LanguageMap = Partial<ChartAsCodeLanguageMap & DashboardAsCodeLanguageMap>;

Examples

// Extract translatable content from a chart
const chartI18n = new ChartAsCodeInternalization();
const languageMap = chartI18n.getLanguageMap(chartAsCode);

// Apply Spanish translations
const translatedChart = chartI18n.merge(spanishTranslations, chartAsCode);

Project Configuration and Roles

function getProjectDirectory(dbtConnection?: DbtProjectConfig): string | undefined;
function loadLightdashProjectConfig(yamlFileContents: string, onLoaded?: (config: LightdashProjectConfig) => Promise<void>): Promise<LightdashProjectConfig>;

function convertOrganizationRoleToProjectRole(organizationRole: OrganizationMemberRole): ProjectMemberRole | undefined;
function convertSpaceRoleToProjectRole(spaceRole: SpaceMemberRole): ProjectMemberRole | undefined;
function getHighestProjectRole(inheritedRoles: Array<OrganizationRole | ProjectRole | GroupRole | SpaceGroupAccessRole>): InheritedProjectRole | undefined;

Validation Schemas

const chartAsCodeSchema: object;
const dashboardAsCodeSchema: object;
const lightdashDbtYamlSchema: object;
const lightdashProjectConfigSchema: object;
const modelAsCodeSchema: object;

Constants

// Session storage
enum SessionStorageKeys {
  SEND_NOW_SCHEDULER_FILTERS = 'sendNowSchedulerFilters',
  SEND_NOW_SCHEDULER_PARAMETERS = 'sendNowSchedulerParameters'
}

// SQL Runner
const MAX_SAFE_INTEGER: number;  // 2_147_483_647

// Metrics Explorer
const METRICS_EXPLORER_DATE_FORMAT: 'YYYY-MM-DD';
const MAX_SEGMENT_DIMENSION_UNIQUE_VALUES: 10;
const DEFAULT_METRICS_EXPLORER_TIME_INTERVAL: TimeFrames.MONTH;
const MAX_METRICS_TREE_NODE_COUNT: number;

Complete Usage Example

import {
  validateEmail,
  snakeCaseName,
  toggleArrayValue,
  hexToRGB,
  isNotNull,
  sleep,
  getItemMap,
  formatRows,
  itemsInMetricQuery,
  getHumanReadableCronExpression,
} from '@lightdash/common';

// Validation
if (validateEmail('user@example.com')) {
  console.log('Valid email');
}

// String transformations
const snake = snakeCaseName('Customer Name');  // "customer_name"

// Array utilities
const tags = ['tag1', 'tag2'];
const newTags = toggleArrayValue(tags, 'tag3');  // ['tag1', 'tag2', 'tag3']

// Type guards
const values: (string | null)[] = ['a', null, 'b'];
const nonNull = values.filter(isNotNull);  // string[]

// Color utilities
const rgb = hexToRGB('#4C8BF5', 0.5);  // "rgba(76, 139, 245, 0.5)"

// Async utilities
await sleep(1000);

// Query result formatting
const itemsMap = getItemMap(explore, additionalMetrics, tableCalculations, customDimensions);
const formattedRows = formatRows(rawRows, itemsMap);

// Scheduler
const readable = getHumanReadableCronExpression('0 9 * * *', 'America/New_York');
// "at 09:00 AM (UTC -05:00)"