General-purpose utility functions for data manipulation, string operations, and type checking.
Converts text to snake_case format.
/**
* Convert text to snake_case format
* @param text - Text to convert
* @returns Snake-cased string
*/
function snakeCaseName(text: string): string;Usage Example:
import { snakeCaseName } from '@lightdash/common';
snakeCaseName('UserProfile'); // 'user_profile'
snakeCaseName('dataSource'); // 'data_source'
snakeCaseName('My Chart Name'); // 'my_chart_name'Checks if text contains special characters.
/**
* Check if text contains special characters (non-alphanumeric, excluding spaces)
* @param text - Text to check
* @returns True if text contains special characters
*/
function hasSpecialCharacters(text: string): boolean;Usage Example:
import { hasSpecialCharacters } from '@lightdash/common';
hasSpecialCharacters('Hello World'); // false
hasSpecialCharacters('Hello@World'); // true
hasSpecialCharacters('test123'); // falseChecks if two arrays have common elements.
/**
* Check if two arrays have any common elements
* @param tags - First array
* @param tags2 - Second array
* @returns True if arrays have intersection
*/
function hasIntersection(tags: string[], tags2: string[]): boolean;Usage Example:
import { hasIntersection } from '@lightdash/common';
hasIntersection(['a', 'b'], ['b', 'c']); // true
hasIntersection(['a', 'b'], ['c', 'd']); // false
hasIntersection(['x'], ['x', 'y', 'z']); // trueAdds or removes a value from an array.
/**
* Toggle a value in an array (add if missing, remove if present)
* @param initialArray - Source array
* @param value - Value to toggle
* @returns New array with value toggled
*/
function toggleArrayValue<T = string>(initialArray: T[], value: T): T[];Usage Example:
import { toggleArrayValue } from '@lightdash/common';
toggleArrayValue(['a', 'b'], 'c'); // ['a', 'b', 'c']
toggleArrayValue(['a', 'b', 'c'], 'b'); // ['a', 'c']
toggleArrayValue([1, 2, 3], 4); // [1, 2, 3, 4]Replaces occurrences of a string in an array.
/**
* Replace all occurrences of a string in an array
* @param arrayToUpdate - Array to update
* @param valueToReplace - Value to find
* @param newValue - Replacement value
* @returns New array with replacements
*/
function replaceStringInArray(
arrayToUpdate: string[],
valueToReplace: string,
newValue: string
): string[];Usage Example:
import { replaceStringInArray } from '@lightdash/common';
replaceStringInArray(
['old', 'test', 'old'],
'old',
'new'
);
// Result: ['new', 'test', 'new']Converts hex color to RGB or RGBA format.
/**
* Convert hex color to RGB or RGBA string
* @param hex - Hex color code (e.g., '#FF5733')
* @param alpha - Optional alpha value (0-1)
* @returns RGB or RGBA string
*/
function hexToRGB(hex: string, alpha: number | undefined): string;Usage Example:
import { hexToRGB } from '@lightdash/common';
hexToRGB('#FF5733', undefined); // 'rgb(255, 87, 51)'
hexToRGB('#FF5733', 0.5); // 'rgba(255, 87, 51, 0.5)'
hexToRGB('#00FF00', 1); // 'rgba(0, 255, 0, 1)'Validates if a string is a valid hex color code.
/**
* Validate if a string is a valid hex color code
* @param color - String to validate
* @returns True if valid hex color (#RGB, #RRGGBB, or #RRGGBBAA)
*/
function isHexCodeColor(color: string): boolean;Usage Example:
import { isHexCodeColor } from '@lightdash/common';
isHexCodeColor('#FF5733'); // true
isHexCodeColor('#FFF'); // true (3-digit hex)
isHexCodeColor('#FF5733AA'); // true (with alpha)
isHexCodeColor('FF5733'); // false (missing #)
isHexCodeColor('red'); // false (not hex)
isHexCodeColor('#GGGGGG'); // false (invalid characters)Validates an array of hex color codes and returns invalid ones.
/**
* Validate an array of hex color codes
* @param colors - Array of strings to validate as hex colors
* @returns Array of invalid colors found, empty if all are valid
*/
function getInvalidHexColors(colors: string[]): string[];Usage Example:
import { getInvalidHexColors } from '@lightdash/common';
const colors = ['#FF5733', '#00FF00', 'red', '#GGGGGG', '#123'];
const invalid = getInvalidHexColors(colors);
console.log(invalid); // ['red', '#GGGGGG']
// Validate before use
const userColors = ['#FF0000', '#00FF00', 'blue'];
const invalidColors = getInvalidHexColors(userColors);
if (invalidColors.length > 0) {
console.error('Invalid colors:', invalidColors);
}Processes an array of strings, trimming whitespace and filtering out empty values.
/**
* Clean an array of strings, trimming and filtering empty values
* @param colors - Array of strings to process
* @returns Cleaned array of strings
*/
function cleanColorArray(colors: string[]): string[];Usage Example:
import { cleanColorArray } from '@lightdash/common';
const rawColors = ['#FF5733', ' #00FF00 ', '', ' ', '#0000FF'];
const cleaned = cleanColorArray(rawColors);
console.log(cleaned); // ['#FF5733', '#00FF00', '#0000FF']Returns the best contrasting text color (black or white) for a given background color.
/**
* Get the best contrasting text color for a background
* Uses APCA contrast algorithm for perception-based contrast
* @param backgroundColor - Hex color string for the background
* @returns 'white' or 'black' depending on which has better contrast
*/
function getReadableTextColor(backgroundColor: string): string;Usage Example:
import { getReadableTextColor } from '@lightdash/common';
getReadableTextColor('#FF5733'); // 'white' (dark red background)
getReadableTextColor('#FFFF00'); // 'black' (yellow background)
getReadableTextColor('#000000'); // 'white' (black background)
getReadableTextColor('#FFFFFF'); // 'black' (white background)
// Use for dynamic styling
const bgColor = userPreferences.backgroundColor;
const textColor = getReadableTextColor(bgColor);
console.log(`Use text color ${textColor} on background ${bgColor}`);Removes undefined and null properties from an object recursively.
/**
* Remove undefined and null properties from an object recursively
* @param object - Object to clean
* @returns New object without empty properties
*/
function removeEmptyProperties(object: Record<string, any>): Record<string, any>;Usage Example:
import { removeEmptyProperties } from '@lightdash/common';
removeEmptyProperties({
name: 'test',
value: null,
nested: {
a: 1,
b: undefined
}
});
// Result: { name: 'test', nested: { a: 1 } }Performs deep equality comparison between two objects.
/**
* Deep equality comparison between two objects
* @param object1 - First object
* @param object2 - Second object
* @returns True if objects are deeply equal
*/
function deepEqual(
object1: Record<string, any>,
object2: Record<string, any>
): boolean;Usage Example:
import { deepEqual } from '@lightdash/common';
deepEqual({ a: 1, b: { c: 2 } }, { a: 1, b: { c: 2 } }); // true
deepEqual({ a: 1 }, { a: 2 }); // false
deepEqual({ a: { b: 1 } }, { a: { b: 2 } }); // falseType guard to filter out null values.
/**
* Type guard to exclude null values
* @param arg - Value to check
* @returns True if value is not null
*/
function isNotNull<T>(arg: T): arg is Exclude<T, null>;Usage Example:
import { isNotNull } from '@lightdash/common';
const values = [1, null, 2, null, 3];
const filtered = values.filter(isNotNull); // Type: number[]
// Result: [1, 2, 3]Type guard for LightdashMode enum values.
/**
* Check if a string is a valid LightdashMode
* @param x - String to check
* @returns True if x is a valid LightdashMode
*/
function isLightdashMode(x: string): x is LightdashMode;Extracts project directory from DBT connection configuration.
/**
* Get project directory from DBT connection configuration
* @param dbtConnection - DBT project configuration
* @returns Project directory path or undefined
*/
function getProjectDirectory(dbtConnection?: DbtProjectConfig): string | undefined;Usage Example:
import { getProjectDirectory, DbtProjectType } from '@lightdash/common';
const dbtConfig = {
type: DbtProjectType.DBT,
project_dir: '/path/to/project'
};
const dir = getProjectDirectory(dbtConfig); // '/path/to/project'Types for working with raw SQL query results.
type SqlResultsRow = { [columnName: string]: unknown };
type SqlResultsField = {
name: string;
type: string;
};
type SqlQueryResults = {
fields: SqlResultsField[];
rows: SqlResultsRow[];
};Zod schema for user profile completion.
const CompleteUserSchema: z.ZodObject<{
organizationName: z.ZodOptional<z.ZodString>;
jobTitle: z.ZodString;
enableEmailDomainAccess: z.ZodDefault<z.ZodBoolean>;
isMarketingOptedIn: z.ZodDefault<z.ZodBoolean>;
isTrackingAnonymized: z.ZodDefault<z.ZodBoolean>;
}>;
type CompleteUserArgs = z.infer<typeof CompleteUserSchema>;Enum for tracking how Lightdash was installed.
enum LightdashInstallType {
DOCKER_IMAGE = 'docker_image',
BASH_INSTALL = 'bash_install',
HEROKU = 'heroku',
UNKNOWN = 'unknown'
}Type for creating hierarchical space structures.
type TreeCreateSpace = CreateSpace & {
children?: TreeCreateSpace[];
groupAccess?: {
groupUuid: string;
role: SpaceMemberRole;
}[];
};Safe access to array and object values with error handling.
/**
* Safely get a value from an array-like object with index bounds checking
* @param obj - Array-like object to access
* @param key - Index to access
* @param errorMessage - Optional custom error message
* @returns Value at the index
* @throws Error if index is out of bounds or object is undefined
*/
function getArrayValue<T>(
obj: ArrayLike<T> | undefined,
key: number,
errorMessage?: string
): T;
/**
* Safely get a value from an object with key existence checking
* @param obj - Object to access
* @param key - Key to access
* @param errorMessage - Optional custom error message
* @returns Value at the key
* @throws Error if key doesn't exist or object is undefined
*/
function getObjectValue<T>(
obj: Record<string | number, T> | undefined,
key: string | number,
errorMessage?: string
): T;
/**
* Type guard to check if an object has a specific property
* @param obj - Object to check
* @param property - Property name to check for
* @returns True if object has the property
*/
function hasProperty<T>(obj: unknown, property: string): obj is { scope: T };Usage:
import { getArrayValue, getObjectValue, hasProperty } from '@lightdash/common';
// Safe array access
const arr = ['a', 'b', 'c'];
const value = getArrayValue(arr, 1); // 'b'
// Safe object access
const obj = { name: 'John', age: 30 };
const name = getObjectValue(obj, 'name'); // 'John'
// Property checking
if (hasProperty(someObject, 'scope')) {
console.log(someObject.scope);
}Generate and manipulate slugs for content-as-code paths.
/**
* Generate a URL-safe slug from a name
* @param name - Name to convert to slug
* @returns URL-safe slug string
*/
function generateSlug(name: string): string;
/**
* Convert a slug to ltree path format (PostgreSQL ltree extension)
* @param slug - Slug to convert
* @returns Ltree path string
*/
function getLtreePathFromSlug(slug: string): string;
/**
* Convert a content-as-code file path to ltree path
* @param path - Content-as-code path
* @returns Ltree path string
*/
function getLtreePathFromContentAsCodePath(path: string): string;
/**
* Convert ltree path back to content-as-code path
* @param path - Ltree path
* @returns Content-as-code path string
*/
function getContentAsCodePathFromLtreePath(path: string): string;
/**
* Get deepest paths from a list (removes parent paths if children exist)
* @param paths - Array of ltree paths
* @returns Array of deepest paths only
*/
function getDeepestPaths(paths: string[]): string[];
/**
* Check if one path is a sub-path of another
* @param path - Parent path
* @param subPath - Potential sub-path
* @returns True if subPath is under path
*/
function isSubPath(path: string, subPath: string): boolean;Usage:
import {
generateSlug,
getLtreePathFromSlug,
getDeepestPaths,
isSubPath,
} from '@lightdash/common';
// Generate slugs
const slug = generateSlug('My Dashboard Name'); // 'my-dashboard-name'
// Ltree path conversion
const ltreePath = getLtreePathFromSlug('parent.child.item');
// Check path relationships
isSubPath('spaces.sales', 'spaces.sales.dashboards'); // true
// Get deepest paths
const paths = ['a', 'a.b', 'a.b.c', 'd', 'd.e'];
const deepest = getDeepestPaths(paths); // ['a.b.c', 'd.e']/**
* Get subtotal cache key from dimension names
* @param dimensions - Array of dimension names
* @returns Cache key string
*/
function getSubtotalKey(dimensions: string[]): string;
/**
* Set a UUID parameter in the URL
* @param key - Parameter key
* @param value - UUID value (or null to remove)
*/
function setUuidParam(key: string, value?: string | null): void;
/**
* Maximum number of nodes in metrics tree visualization
*/
const MAX_METRICS_TREE_NODE_COUNT: number; // Value: 30Test and development seed data for consistent testing.
// Organization seed data
const SEED_ORG_1: {
organization_uuid: string;
organization_name: string;
};
// User seed data
const SEED_ORG_1_ADMIN: {
user_uuid: string;
first_name: string;
last_name: string;
is_marketing_opted_in: boolean;
is_tracking_anonymized: boolean;
is_setup_complete: boolean;
is_active: boolean;
};
const SEED_ORG_1_ADMIN_EMAIL: {
email: string;
is_primary: boolean;
};
const SEED_ORG_1_ADMIN_PASSWORD: {
password: string;
};
const SEED_ORG_1_ADMIN_ROLE: OrganizationMemberRole;
// Additional user seeds
const SEED_ORG_1_EDITOR: typeof SEED_ORG_1_ADMIN;
const SEED_ORG_1_VIEWER: typeof SEED_ORG_1_ADMIN;
const SEED_ORG_2_ADMIN: typeof SEED_ORG_1_ADMIN;
// Project and space seeds
const SEED_PROJECT: {
project_uuid: string;
name: string;
project_type: ProjectType;
dbt_connection_type: DbtProjectType;
dbt_connection: null;
copied_from_project_uuid: null;
organization_warehouse_credentials_uuid: null;
};
const SEED_SPACE: {
name: string;
};
// Group seeds
const SEED_GROUP: {
groupUuid: string;
name: string;
};
const SEED_EMBED_SECRET: string;
// Space tree structures for testing
const SPACE_TREE_1: readonly TreeCreateSpace[];
const SPACE_TREE_2: readonly TreeCreateSpace[];