Various utility functions that don't fit into other specialized categories, including boolean conversion, sleep utilities, type utilities, Lightdash mode helpers, and additional miscellaneous functions.
This module provides the following functionality:
/**
* Converts various types of values to boolean, with special handling for string representations
* of boolean values that should be interpreted literally rather than using JavaScript's
* truthy/falsy evaluation
* @param value - The value to convert to boolean
* @returns The boolean representation of the value
*/
function convertToBooleanValue(value: unknown): boolean;Example:
import { convertToBooleanValue } from '@lightdash/common';
// String representations
console.log(convertToBooleanValue('true')); // true
console.log(convertToBooleanValue('false')); // false
console.log(convertToBooleanValue('FALSE')); // false (case-insensitive)
console.log(convertToBooleanValue(' True ')); // true (handles whitespace)
// Native booleans pass through
console.log(convertToBooleanValue(true)); // true
console.log(convertToBooleanValue(false)); // false
// Other types use truthy/falsy evaluation
console.log(convertToBooleanValue(1)); // true
console.log(convertToBooleanValue(0)); // false
console.log(convertToBooleanValue('hello')); // true
console.log(convertToBooleanValue('')); // false
console.log(convertToBooleanValue(null)); // false
console.log(convertToBooleanValue(undefined)); // false
// Use for parsing configuration values
interface AppConfig {
enabled: boolean;
}
function parseConfig(raw: Record<string, unknown>): AppConfig {
return {
enabled: convertToBooleanValue(raw.enabled),
};
}
// Handles string "false" correctly (unlike !!)
const config1 = parseConfig({ enabled: 'false' }); // { enabled: false }
const config2 = parseConfig({ enabled: 'true' }); // { enabled: true }/**
* Sleep for a specified number of milliseconds
* @param ms - Number of milliseconds to sleep
* @returns Promise that resolves after the specified time
*/
function sleep(ms: number): Promise<void>;Example:
import { sleep } from '@lightdash/common';
async function delayedOperation() {
console.log('Starting...');
await sleep(1000); // Wait 1 second
console.log('Done!');
}
// Use in retry logic
async function retryOperation<T>(
operation: () => Promise<T>,
maxRetries: number = 3
): Promise<T> {
for (let i = 0; i < maxRetries; i++) {
try {
return await operation();
} catch (error) {
if (i === maxRetries - 1) throw error;
await sleep(1000 * (i + 1)); // Exponential backoff
}
}
throw new Error('Max retries exceeded');
}/**
* Type guard to check if a string is a valid Lightdash mode
* @param x - String to check
* @returns true if x is a valid LightdashMode
*/
function isLightdashMode(x: string): x is LightdashMode;
/**
* Lightdash installation types for analytics
*/
enum LightdashInstallType {
DOCKER_IMAGE = 'docker_image',
BASH_INSTALL = 'bash_install',
HEROKU = 'heroku',
UNKNOWN = 'unknown',
}Example:
import { isLightdashMode, LightdashMode, LightdashInstallType } from '@lightdash/common';
// Type guard for validating mode strings
const modeString = process.env.LIGHTDASH_MODE;
if (isLightdashMode(modeString)) {
// modeString is now typed as LightdashMode
console.log('Valid mode:', modeString);
}
// Track installation type for telemetry
const installType = LightdashInstallType.DOCKER_IMAGE;
function getInstallType(): LightdashInstallType {
if (process.env.DOCKER_IMAGE) {
return LightdashInstallType.DOCKER_IMAGE;
} else if (process.env.BASH_INSTALL) {
return LightdashInstallType.BASH_INSTALL;
} else if (process.env.HEROKU) {
return LightdashInstallType.HEROKU;
}
return LightdashInstallType.UNKNOWN;
}/**
* Extract argument types from a function
*/
type ArgumentsOf<F extends Function> = F extends (...args: infer A) => any
? A
: never;Example:
import { type ArgumentsOf } from '@lightdash/common';
// Extract argument types
function exampleFunc(name: string, age: number, active: boolean) {
// ...
}
type ExampleArgs = ArgumentsOf<typeof exampleFunc>;
// Type: [name: string, age: number, active: boolean]
// Use with generic function wrappers
function wrapFunction<F extends Function>(
fn: F,
...args: ArgumentsOf<F>
): ReturnType<F> {
console.log('Calling function with args:', args);
return fn(...args);
}/**
* Converts an additional metric to a compiled metric
* @param additionalMetric - Additional metric to convert
* @param table - Table context for the metric
* @returns Compiled metric
*/
function convertAdditionalMetric(
additionalMetric: AdditionalMetric,
table: Table
): CompiledMetric;
/**
* Returns available metric types for a given dimension type
* Different dimension types support different metric aggregations
* @param type - Dimension type
* @returns Array of supported metric types
*/
function getCustomMetricType(type: DimensionType): MetricType[];Example:
import { getCustomMetricType, DimensionType, MetricType } from '@lightdash/common';
// 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]
// Get available metrics for a boolean dimension
const booleanMetrics = getCustomMetricType(DimensionType.BOOLEAN);
// Returns: [COUNT_DISTINCT, COUNT]
// Use when building custom metric UI
function getAvailableAggregations(dimension: Dimension): MetricType[] {
return getCustomMetricType(dimension.type);
}Functions for working with explores, fields, dimensions, metrics, and items maps.
/**
* Gets all fields (dimensions and metrics) from an explore
*/
function getFields(explore: Explore): CompiledField[];
/**
* Gets only dimension fields from an explore
*/
function getDimensions(explore: Explore): CompiledDimension[];
/**
* Gets only metric fields from an explore
*/
function getMetrics(explore: Explore): CompiledMetric[];
/**
* Gets only visible (non-hidden) fields from an explore
* @param explore - The explore containing fields
* @returns Array of compiled fields that are not marked as hidden
*/
function getVisibleFields(explore: Explore): CompiledField[];Usage example:
import { getVisibleFields, type Explore } from '@lightdash/common';
const visibleFields = getVisibleFields(explore);
// Returns only fields where hidden !== true
console.log(`Found ${visibleFields.length} visible fields`);/**
* Finds a field by its ID in an explore
* @param explore - The explore to search within
* @param id - The field ID (format: "tableName_fieldName")
* @returns The matching field, or undefined if not found
*/
function findFieldByIdInExplore(explore: Explore, id: FieldId): Field | undefined;Usage example:
import { findFieldByIdInExplore, getItemId, type Explore } from '@lightdash/common';
const fieldId = 'orders_order_date';
const field = findFieldByIdInExplore(explore, fieldId);
if (field) {
console.log(`Found field: ${field.label}`);
} else {
console.log('Field not found');
}/**
* Creates a map of field IDs to field objects
*/
function getFieldMap(
explore: Explore,
additionalMetrics?: AdditionalMetric[]
): Record<string, CompiledField | AdditionalMetric>;
/**
* Creates a comprehensive items map including fields, table calculations, and custom dimensions
*/
function getItemMap(
explore: Explore,
additionalMetrics?: AdditionalMetric[],
tableCalculations?: TableCalculation[],
customDimensions?: CustomDimension[]
): ItemsMap;
/**
* Extracts only dimensions from an items map
*/
function getDimensionsFromItemsMap(itemsMap: ItemsMap): Record<string, Dimension | CustomDimension>;
/**
* Extracts only filterable dimensions from an items map
*/
function getFilterableDimensionsFromItemsMap(itemsMap: ItemsMap): Record<string, FilterableDimension>;
/**
* Extracts only metrics from an items map with optional filter
*/
function getMetricsFromItemsMap(
itemsMap: ItemsMap,
filter?: (value: ItemsMap[string]) => boolean
): Record<string, Metric>;
/**
* Extracts only table calculations from an items map
*/
function getTableCalculationsFromItemsMap(itemsMap?: ItemsMap): Record<string, TableCalculation>;/**
* Generates a subtotal key from dimension array for subtotal grouping
* @param dimensions - Array of dimension field IDs
* @returns Colon-separated key string (e.g., "dim1:dim2:dim3")
*/
function getSubtotalKey(dimensions: string[]): string;Example:
import { getSubtotalKey } from '@lightdash/common';
// Generate subtotal key for dimension combination
const dimensions = ['customers_region', 'customers_country', 'customers_city'];
const key = getSubtotalKey(dimensions);
// Returns: "customers_region:customers_country:customers_city"
// Use for subtotal row identification
const subtotalRow = {
...row,
[SUBTOTAL_KEY_FIELD]: getSubtotalKey(['customers_region', 'customers_country']),
};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>;Example:
import { CompleteUserSchema, type CompleteUserArgs } from '@lightdash/common';
// Validate user completion data
const userData: CompleteUserArgs = {
organizationName: 'My Company',
jobTitle: 'Data Analyst',
enableEmailDomainAccess: false,
isMarketingOptedIn: true,
isTrackingAnonymized: false,
};
const validated = CompleteUserSchema.parse(userData);
// In onboarding flow
async function completeUserOnboarding(data: unknown) {
try {
const validatedData = CompleteUserSchema.parse(data);
await updateUser(validatedData);
} catch (error) {
console.error('Invalid user data:', error);
}
}/**
* Get organization name validation schema
* @returns Zod schema for organization name validation
*/
function getOrganizationNameSchema(): ZodSchema;// Enterprise edition functionality
export * from './ee/AiAgent';
export * from './ee/commercialFeatureFlags';
export * from './ee/embed';
export * from './ee/scim/errors';
export * from './ee/scim/types';
export * from './ee/serviceAccounts/types';
enum ScimSchemaType {
ERROR = 'urn:ietf:params:scim:api:messages:2.0:Error',
USER = 'urn:ietf:params:scim:schemas:core:2.0:User',
GROUP = 'urn:ietf:params:scim:schemas:core:2.0:Group',
ROLE = 'urn:ietf:params:scim:schemas:extension:2.0:Role',
LIST_RESPONSE = 'urn:ietf:params:scim:api:messages:2.0:ListResponse',
SCHEMA = 'urn:ietf:params:scim:schemas:core:2.0:Schema',
PATCH = 'urn:ietf:params:scim:api:messages:2.0:PatchOp',
LIGHTDASH_USER_EXTENSION = 'urn:lightdash:params:scim:schemas:extension:2.0:User',
SERVICE_PROVIDER_CONFIG = 'urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig',
RESOURCE_TYPE = 'urn:ietf:params:scim:schemas:core:2.0:ResourceType',
}Example:
import { ScimSchemaType } from '@lightdash/common';
// SCIM user provisioning
const scimUser = {
schemas: [ScimSchemaType.USER],
userName: 'john.doe@example.com',
active: true,
};
// Check for enterprise features
import { CommercialFeatureFlags } from '@lightdash/common';
if (organization.features.includes(CommercialFeatureFlags.AI_AGENT)) {
// Enable AI agent features
}class ChangesetUtils {
static applyChangeset<C extends ChangeBase>(
changeset: { changes: C[] },
explores: Record<string, Explore | ExploreError>
): Record<string, Explore | ExploreError>;
}Example:
import { ChangesetUtils } from '@lightdash/common';
const updatedExplores = ChangesetUtils.applyChangeset(
{ changes: [/* array of changes */] },
explores
);/**
* Parses a conditional formatting expression into an AST
* @param expression - The expression string
* @returns Parsed expression AST
*/
function parseConditionalFormatExpression(expression: string): ConditionalFormatExpression;
/**
* Evaluates a conditional formatting expression against a value
* @param expression - The parsed expression
* @param value - The value to evaluate
* @returns true if the condition matches
*/
function evaluateConditionalFormatExpression(
expression: ConditionalFormatExpression,
value: unknown
): boolean;Example:
import {
parseConditionalFormatExpression,
evaluateConditionalFormatExpression,
} from '@lightdash/common';
// Parse an expression
const expr = parseConditionalFormatExpression('value > 100');
// Evaluate against values
const matches = evaluateConditionalFormatExpression(expr, 150); // truetype SqlResultsRow = { [columnName: string]: unknown };
type SqlResultsField = {
name: string;
type: string;
};
type SqlQueryResults = {
fields: SqlResultsField[];
rows: SqlResultsRow[];
};Example:
import { type SqlQueryResults } from '@lightdash/common';
// Raw warehouse results
const sqlResults: SqlQueryResults = {
fields: [
{ name: 'customer_id', type: 'integer' },
{ name: 'total_revenue', type: 'numeric' }
],
rows: [
{ customer_id: 1, total_revenue: 1234.56 },
{ customer_id: 2, total_revenue: 2345.67 }
]
};
// These are then transformed by formatRows() into ResultRow formatimport {
convertToBooleanValue,
sleep,
isLightdashMode,
getCustomMetricType,
getFields,
getItemMap,
getSubtotalKey,
type ArgumentsOf,
type SqlQueryResults,
LightdashInstallType,
} from '@lightdash/common';
// Boolean conversion
const enabled = convertToBooleanValue(process.env.FEATURE_ENABLED);
// Sleep utility
async function performOperation() {
await sleep(1000);
return apiCall();
}
// Mode validation
if (isLightdashMode('default')) {
console.log('Valid Lightdash mode');
}
// Metric types
const numberMetrics = getCustomMetricType(DimensionType.NUMBER);
// Explore utilities
const fields = getFields(explore);
const itemsMap = getItemMap(explore, additionalMetrics, tableCalculations);
// Subtotal key
const key = getSubtotalKey(['region', 'country', 'city']);
// Type utilities
type MyFunctionArgs = ArgumentsOf<typeof myFunction>;
// Installation type
const installType = LightdashInstallType.DOCKER_IMAGE;