This module provides comprehensive validation, type checking, and assertion utilities for the Lightdash platform. It includes email/password validation, organization name validation, type guards, assertion helpers, and JSON schema validation for configuration files.
This module provides the following functionality:
This module uses Zod for schema validation. Install it separately:
npm install zod// Zod types used by validation functions
// These types come from the 'zod' package
import type { z } from 'zod';
type ZodSchema = z.ZodType<any, any, any>;/**
* 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 using String.match()
* @param value - The domain to validate
* @returns RegExpMatchArray if domain is valid, null otherwise
*/
function isValidEmailDomain(value: string): RegExpMatchArray | null;
/**
* 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 (returns RegExpMatchArray or null)
const domainMatch = isValidEmailDomain('company.com');
if (domainMatch) {
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)/**
* Validates a password string
* Requires: minimum 8 characters, at least one letter, at least one number or symbol
* @param password - Password to validate
* @returns true if password meets requirements
*/
function validatePassword(password: string): boolean;
/**
* Returns a Zod schema for password validation
* Password must be at least 8 characters, contain a letter, and contain a number or symbol
* @returns Zod schema for password validation
*/
function getPasswordSchema(): z.ZodEffects<z.ZodString>;Example:
import { validatePassword, getPasswordSchema } from '@lightdash/common';
// Basic validation
if (validatePassword('SecurePass123!')) {
console.log('Valid password');
}
// Use with Zod
const schema = z.object({
password: getPasswordSchema(),
});/**
* Zod schema for completing user setup with organization details
* Used during user onboarding to collect additional information
*/
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 inferred from CompleteUserSchema
*/
type CompleteUserArgs = z.infer<typeof CompleteUserSchema>;Example:
import { CompleteUserSchema, type CompleteUserArgs } from '@lightdash/common';
const completeUserData: CompleteUserArgs = {
organizationName: 'My Company',
jobTitle: 'Data Analyst',
enableEmailDomainAccess: false,
isMarketingOptedIn: true,
isTrackingAnonymized: false,
};
// Validate with Zod
const result = CompleteUserSchema.safeParse(completeUserData);
if (result.success) {
console.log('User data is valid');
}/**
* Validates an organization name
* @param name - Organization name to validate
* @returns true if the name is valid (letters, numbers, spaces, underscores, dashes only)
*/
function validateOrganizationName(name: string): boolean;
/**
* Returns a Zod schema for organization name validation
* @returns Zod schema that validates organization names
*/
function getOrganizationNameSchema(): ZodSchema;
/**
* Validates organization name and throws a ParameterError if invalid
* @param name - Organization name to validate
* @throws ParameterError if the name is invalid
*/
function validateOrganizationNameOrThrow(name: string): void;Example:
import {
validateOrganizationName,
getOrganizationNameSchema,
validateOrganizationNameOrThrow
} from '@lightdash/common';
// Basic validation
if (validateOrganizationName('My Company')) {
console.log('Valid organization name');
}
// With Zod
const schema = z.object({
orgName: getOrganizationNameSchema(),
});
// Validate and throw on error
try {
validateOrganizationNameOrThrow('Invalid@Name');
} catch (error) {
console.error('Invalid organization name');
}/**
* Type guard that filters out null values from arrays
* Note: Only filters null, NOT undefined. Use with caution if your data may contain undefined.
* @param arg - Value to check
* @returns true if value is not null
*/
function isNotNull<T>(arg: T): arg is Exclude<T, null>;
/**
* Type guard to check if a value is an API error response
* @param error - Value to check
* @returns true if value is an ApiError
*/
function isApiError(error: unknown): error is ApiError;
/**
* Type guard to check if an account is a JWT user
* @param account - Account to check (optional)
* @returns true if account exists and is a JWT-authenticated user
*/
function isJwtUser(account?: Account): account is AnonymousAccount;
function isLightdashMode(x: string): x is LightdashMode;Example:
import { isNotNull } from '@lightdash/common';
// Filters only null values
const values: (string | null)[] = ['a', null, 'b', null, 'c'];
const nonNull = values.filter(isNotNull);
// Returns: ['a', 'b', 'c'] with type string[]
// Important: isNotNull does NOT filter undefined
const valuesWithUndefined: (string | null | undefined)[] = ['a', null, 'b', undefined, 'c'];
const filtered = valuesWithUndefined.filter(isNotNull);
// Returns: ['a', 'b', undefined, 'c'] - undefined is NOT filtered!function assertUnreachable(_x: never, error: string | Error): never;Example:
import assertUnreachable from '@lightdash/common';
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, 'Unknown status');
// TypeScript will error if any status values are not handled
}
}JSON Schema definitions for validating "as-code" YAML configuration files for charts, dashboards, DBT models, and project configuration.
const chartAsCodeSchema: object;
const dashboardAsCodeSchema: object;
const lightdashDbtYamlSchema: object;
const lightdashProjectConfigSchema: object;
const modelAsCodeSchema: object;These are JSON Schema files exported for external validation of Lightdash configuration files.
Example:
import {
chartAsCodeSchema,
dashboardAsCodeSchema,
lightdashProjectConfigSchema,
} from '@lightdash/common';
// External dependency - Install separately: npm install ajv
import Ajv from 'ajv';
const ajv = new Ajv();
// Validate a chart-as-code YAML file
const validateChart = ajv.compile(chartAsCodeSchema);
const isValid = validateChart(chartConfig);
if (!isValid) {
console.error('Chart validation errors:', validateChart.errors);
}
// Validate dashboard-as-code configuration
const validateDashboard = ajv.compile(dashboardAsCodeSchema);
const dashboardValid = validateDashboard(dashboardConfig);
// Validate project configuration
const validateProject = ajv.compile(lightdashProjectConfigSchema);
const projectValid = validateProject(projectConfig);The package provides URL templating utilities for dynamic URL generation using Liquid template syntax.
/**
* Renders a templated URL with values from a result row
* @param templatedUrl - Template string using ${} syntax
* @param value - Current cell value
* @param row - Complete row data
* @returns Rendered URL string
*/
function renderTemplatedUrl(
templatedUrl: string,
value: ResultValue,
row: Record<string, Record<string, ResultValue>>
): string;Usage:
import { renderTemplatedUrl } from '@lightdash/common';
const templatedUrl = 'https://example.com/user/${row.users_user_id.raw}';
const value = { raw: 'John', formatted: 'John' };
const row = {
'users_user_id': { raw: 123, formatted: '123' },
'users_name': { raw: 'John', formatted: 'John' }
};
const url = renderTemplatedUrl(templatedUrl, value, row);
// Result: 'https://example.com/user/123'/**
* Extracts row field dependencies from a templated URL
* @param templatedUrl - Template string to analyze
* @returns Array of field names referenced in template
*/
function getTemplatedUrlRowDependencies(templatedUrl: string): string[];Usage:
import { getTemplatedUrlRowDependencies } from '@lightdash/common';
const templatedUrl = 'https://example.com/user/${row.users_user_id.raw}?name=${row.users_name.formatted}';
const dependencies = getTemplatedUrlRowDependencies(templatedUrl);
// Result: ['users_user_id', 'users_name']Types for handling validation errors in charts, dashboards, and tables.
/**
* Enum for different types of validation errors
*/
enum ValidationErrorType {
Chart = 'chart',
Sorting = 'sorting',
Filter = 'filter',
Metric = 'metric',
Model = 'model',
Dimension = 'dimension',
CustomMetric = 'custom metric',
}
/**
* Enum for dashboard filter-specific validation errors
*/
enum DashboardFilterValidationErrorType {
FieldDoesNotExist = 'field_does_not_exist',
TableNotUsedByAnyChart = 'table_not_used_by_any_chart',
TableDoesNotExist = 'table_does_not_exist',
}
/**
* Enum for validation source types (what entity is being validated)
*/
enum ValidationSourceType {
Chart = 'chart',
Dashboard = 'dashboard',
Table = 'table',
}
/**
* Enum for validation targets (what to validate)
*/
enum ValidationTarget {
CHARTS = 'charts',
DASHBOARDS = 'dashboards',
TABLES = 'tables',
}
/**
* Base validation response with common fields
*/
type ValidationResponseBase = {
validationId: number;
createdAt: Date;
name: string;
error: string;
errorType: ValidationErrorType;
projectUuid: string;
spaceUuid?: string;
source?: ValidationSourceType;
};
/**
* Validation error response for chart-specific errors
*/
type ValidationErrorChartResponse = ValidationResponseBase & {
chartUuid: string | undefined; // Can be undefined if private content
chartKind?: ChartKind;
fieldName?: string;
lastUpdatedBy?: string;
lastUpdatedAt?: Date;
chartViews: number;
chartName?: string;
};
/**
* Validation error response for dashboard-specific errors
*/
type ValidationErrorDashboardResponse = ValidationResponseBase & {
dashboardUuid: string | undefined; // Can be undefined if private content
chartName?: string;
fieldName?: string;
tableName?: string; // For dashboard filter errors referencing specific tables
dashboardFilterErrorType?: DashboardFilterValidationErrorType;
lastUpdatedBy?: string;
lastUpdatedAt?: Date;
dashboardViews: number;
};
/**
* Validation error response for table-specific errors
*/
type ValidationErrorTableResponse = Omit<ValidationResponseBase, 'name'> & {
name: string | undefined;
};
/**
* Union type of all validation error responses
*/
type ValidationResponse =
| ValidationErrorChartResponse
| ValidationErrorDashboardResponse
| ValidationErrorTableResponse;
/**
* Compact validation summary with essential error information
* Used for displaying validation errors in lists or summaries
*/
type ValidationSummary = Pick<
ValidationResponse,
'error' | 'createdAt' | 'validationId'
>;
/**
* Payload for creating a table validation error
*/
type CreateTableValidation = Pick<
ValidationErrorTableResponse,
'error' | 'errorType' | 'projectUuid' | 'name' | 'source'
> & {
modelName: string;
};
/**
* Payload for creating a chart validation error
*/
type CreateChartValidation = Pick<
ValidationErrorChartResponse,
| 'error'
| 'errorType'
| 'fieldName'
| 'name'
| 'projectUuid'
| 'chartUuid'
| 'source'
| 'chartName'
>;
/**
* Payload for creating a dashboard validation error
*/
type CreateDashboardValidation = Pick<
ValidationErrorDashboardResponse,
| 'error'
| 'errorType'
| 'fieldName'
| 'name'
| 'projectUuid'
| 'dashboardUuid'
| 'chartName'
| 'source'
>;
/**
* Union type of all validation creation payloads
*/
type CreateValidation =
| CreateTableValidation
| CreateChartValidation
| CreateDashboardValidation;
/**
* API response for validation requests
*/
type ApiValidateResponse = {
status: 'ok';
results: ValidationResponse[];
};
/**
* API response for dismissing validation errors
*/
type ApiValidationDismissResponse = {
status: 'ok';
};
/**
* Type guard to check if a validation error is a table error
* @param error - Validation error or creation payload to check
* @returns true if error is a table validation error
*/
function isTableValidationError(
error: ValidationResponse | CreateValidation
): error is ValidationErrorTableResponse | CreateTableValidation;
/**
* Type guard to check if a validation error is a chart error
* @param error - Validation error or creation payload to check
* @returns true if error is a chart validation error
*/
function isChartValidationError(
error: ValidationResponse | CreateValidation
): error is ValidationErrorChartResponse | CreateChartValidation;
/**
* Type guard to check if a validation error is a dashboard error
* @param error - Validation error or creation payload to check
* @returns true if error is a dashboard validation error
*/
function isDashboardValidationError(
error: ValidationResponse | CreateValidation
): error is ValidationErrorDashboardResponse | CreateDashboardValidation;
/**
* Validates that a string is a valid ValidationTarget enum value
* @param validationTarget - String to validate
* @returns true if validationTarget is a valid ValidationTarget
*/
function isValidationTargetValid(validationTarget: string): boolean;Example:
import {
ValidationSummary,
ValidationResponse,
ValidationErrorType,
ValidationSourceType,
isChartValidationError,
isDashboardValidationError,
isTableValidationError,
} from '@lightdash/common';
// Handle validation errors by type
function handleValidationError(error: ValidationResponse) {
if (isChartValidationError(error)) {
console.log(`Chart error: ${error.chartName} - ${error.error}`);
console.log(`Chart views: ${error.chartViews}`);
} else if (isDashboardValidationError(error)) {
console.log(`Dashboard error: ${error.dashboardUuid} - ${error.error}`);
console.log(`Dashboard views: ${error.dashboardViews}`);
} else if (isTableValidationError(error)) {
console.log(`Table error: ${error.name} - ${error.error}`);
}
}
// Create a validation summary for display
function createSummary(response: ValidationResponse): ValidationSummary {
return {
error: response.error,
createdAt: response.createdAt,
validationId: response.validationId,
};
}
// Example: Creating a chart validation
const chartValidation: CreateChartValidation = {
error: 'Field "revenue" does not exist in explore',
errorType: ValidationErrorType.Dimension,
fieldName: 'revenue',
name: 'Sales Dashboard',
projectUuid: 'project-123',
chartUuid: 'chart-456',
source: ValidationSourceType.Chart,
chartName: 'Revenue Over Time',
};JSON schemas for validating configuration files, dbt YAML files, and charts/dashboards-as-code.
/**
* JSON schema for chart-as-code validation (version 1.0)
* Use with JSON validators to ensure chart-as-code files conform to expected structure
*/
const chartAsCodeSchema: JSONSchema;
/**
* JSON schema for dashboard-as-code validation (version 1.0)
* Use with JSON validators to ensure dashboard-as-code files conform to expected structure
*/
const dashboardAsCodeSchema: JSONSchema;
/**
* JSON schema for Lightdash dbt YAML configuration (version 2.0)
* Validates lightdash.yml files in dbt projects for proper structure and properties
*/
const lightdashDbtYamlSchema: JSONSchema;
/**
* JSON schema for Lightdash project configuration (version 1.0)
* Validates lightdash_config.yml files for project-level settings
*/
const lightdashProjectConfigSchema: JSONSchema;
/**
* JSON schema for model-as-code validation (version 1.0)
* Use with JSON validators to ensure model definitions conform to expected structure
*/
const modelAsCodeSchema: JSONSchema;Usage:
import {
chartAsCodeSchema,
dashboardAsCodeSchema,
lightdashDbtYamlSchema,
lightdashProjectConfigSchema,
modelAsCodeSchema,
} from '@lightdash/common';
// External dependency - Install separately: npm install ajv
import Ajv from 'ajv';
const ajv = new Ajv();
// Validate a chart-as-code file
const validate = ajv.compile(chartAsCodeSchema);
const valid = validate(chartData);
if (!valid) {
console.error('Validation errors:', validate.errors);
}
// Validate dbt YAML configuration
const validateDbtYaml = ajv.compile(lightdashDbtYamlSchema);
const dbtYamlValid = validateDbtYaml(dbtYamlData);