UNPKG

@shopana/ga

Version:

Type-safe Google Analytics 4 (GA4) tracking library for React and Next.js with ecommerce support, event batching, and SSR compatibility

89 lines 3.46 kB
export class ValidationError extends Error { constructor(message, context) { super(message); this.context = context; this.name = 'ValidationError'; } } const MAX_EVENT_NAME_LENGTH = 40; const MAX_PARAMETER_NAME_LENGTH = 40; const MAX_PARAMETER_VALUE_LENGTH = 100; const MEASUREMENT_ID_REGEX = /^[Gg]-[A-Za-z0-9]+$/; const EVENT_NAME_REGEX = /^[a-zA-Z][a-zA-Z0-9_.]*$/; const PARAMETER_NAME_REGEX = /^[a-zA-Z][a-zA-Z0-9_]*$/; export function validateMeasurementId(id) { if (!id || typeof id !== 'string') { throw new ValidationError('Measurement ID is required', { id }); } if (!MEASUREMENT_ID_REGEX.test(id)) { throw new ValidationError('Invalid GA4 measurement ID format', { id }); } } export function validateEventName(name) { if (!name || typeof name !== 'string') { throw new ValidationError('Event name is required', { name }); } if (name.length > MAX_EVENT_NAME_LENGTH) { throw new ValidationError(`Event name exceeds maximum length of ${MAX_EVENT_NAME_LENGTH}`, { name, length: name.length }); } if (!EVENT_NAME_REGEX.test(name)) { throw new ValidationError('Invalid event name format (must start with letter, contain only letters, numbers, underscores, and dots)', { name }); } } export function validateParameters(params) { Object.entries(params).forEach(([key, value]) => { if (key.length > MAX_PARAMETER_NAME_LENGTH) { throw new ValidationError(`Parameter name exceeds maximum length of ${MAX_PARAMETER_NAME_LENGTH}`, { key, length: key.length }); } if (!PARAMETER_NAME_REGEX.test(key)) { throw new ValidationError('Invalid parameter name format (must start with letter, contain only letters, numbers, and underscores)', { key }); } validateParameterValue(key, value); }); } function validateParameterValue(key, value, depth = 0) { if (depth > 2) { throw new ValidationError('Parameter nesting too deep', { key }); } if (value === null || value === undefined) { return; } if (typeof value === 'string') { if (value.length > MAX_PARAMETER_VALUE_LENGTH) { throw new ValidationError(`Parameter value exceeds maximum length of ${MAX_PARAMETER_VALUE_LENGTH}`, { key, length: value.length }); } return; } if (typeof value === 'number' || typeof value === 'boolean') { return; } if (value instanceof Date) { return; } if (Array.isArray(value)) { value.forEach((item, index) => { if (!isPrimitive(item) && !(item instanceof Date)) { throw new ValidationError('Array elements must be primitive values or Dates', { key, index, type: typeof item }); } }); return; } throw new ValidationError('Parameter value must be a primitive, Date, or array of primitives', { key, type: typeof value }); } export function validateEventPayload(payload) { if (payload.measurementId) { validateMeasurementId(payload.measurementId); } validateEventName(payload.name); if (payload.params) { validateParameters(payload.params); } } function isPrimitive(value) { return (value === null || value === undefined || typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean'); } //# sourceMappingURL=validation.js.map