tdpw
Version:
CLI tool for uploading Playwright test reports to TestDino platform with Azure storage support
200 lines • 7.3 kB
JavaScript
;
/**
* Validation utilities for configuration and input
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ValidationUtils = void 0;
const zod_1 = require("zod");
const types_1 = require("../types");
/**
* Configuration validation utilities
*/
class ValidationUtils {
/**
* Validate data against a Zod schema with user-friendly error messages
*/
static validateSchema(schema, data, context) {
try {
const validated = schema.parse(data);
return {
success: true,
data: validated,
};
}
catch (error) {
if (error instanceof zod_1.z.ZodError) {
const errors = error.issues.map(issue => {
const path = issue.path.length > 0 ? issue.path.join('.') : 'root';
return `${path}: ${issue.message}`;
});
return {
success: false,
errors,
};
}
return {
success: false,
errors: [`Unexpected validation error in ${context}: ${String(error)}`],
};
}
}
/**
* Validate and throw appropriate error with context
*/
static validateOrThrow(schema, data, context) {
const result = this.validateSchema(schema, data, context);
if (!result.success) {
throw new types_1.ValidationError(`Validation failed for ${context}: ${result.errors?.join(', ')}`);
}
return result.data;
}
/**
* Validate API token format with detailed feedback
*/
static validateApiToken(token) {
if (!token) {
throw new types_1.ValidationError('API token is required');
}
if (token.length < 10) {
throw new types_1.ValidationError('API token is too short');
}
const tokenPattern = /^trx_(development|staging|production)_[a-f0-9]{64}$/;
if (!tokenPattern.test(token)) {
const parts = token.split('_');
if (parts.length !== 3) {
throw new types_1.ValidationError('API token must have 3 parts separated by underscores: trx_{environment}_{key}');
}
const prefix = parts[0];
const environment = parts[1];
const key = parts[2];
if (prefix !== 'trx') {
throw new types_1.ValidationError(`API token must start with "trx", found "${prefix}"`);
}
if (!environment || !['development', 'staging', 'production'].includes(environment)) {
throw new types_1.ValidationError(`Invalid environment "${environment || 'empty'}". Must be: development, staging, or production`);
}
if (!key || !/^[a-f0-9]{64}$/.test(key)) {
throw new types_1.ValidationError('API token key must be 64 lowercase hexadecimal characters');
}
}
}
/**
* Validate URL format with helpful feedback
*/
static validateUrl(url, context) {
if (!url) {
throw new types_1.ValidationError(`${context} URL is required`);
}
try {
const parsed = new URL(url);
if (!['http:', 'https:'].includes(parsed.protocol)) {
throw new types_1.ValidationError(`${context} URL must use HTTP or HTTPS protocol, found: ${parsed.protocol}`);
}
if (!parsed.hostname) {
throw new types_1.ValidationError(`${context} URL must have a valid hostname`);
}
}
catch (error) {
if (error instanceof types_1.ValidationError) {
throw error;
}
throw new types_1.ValidationError(`Invalid ${context} URL format: ${url}`);
}
}
/**
* Validate file path exists and is accessible
*/
static validateFilePath(path, context) {
if (!path) {
throw new types_1.ValidationError(`${context} path is required`);
}
if (path.includes('..')) {
throw new types_1.ValidationError(`${context} path cannot contain parent directory references (..)`);
}
if (path.startsWith('/') && !process.platform.startsWith('win')) {
console.warn(`⚠️ Using absolute path for ${context}: ${path}`);
}
}
/**
* Validate Node.js version requirement
*/
static validateNodeVersion(requiredVersion = '18.0.0') {
const currentVersion = process.version;
const current = this.parseVersion(currentVersion.slice(1)); // Remove 'v' prefix
const required = this.parseVersion(requiredVersion);
if (this.compareVersions(current, required) < 0) {
throw new types_1.ConfigurationError(`Node.js ${requiredVersion} or higher is required. ` +
`Current version: ${currentVersion}`);
}
}
/**
* Parse semantic version string
*/
static parseVersion(version) {
const parts = version.split('.').map(Number);
return [parts[0] || 0, parts[1] || 0, parts[2] || 0];
}
/**
* Compare two semantic versions
* Returns: -1 if a < b, 0 if a === b, 1 if a > b
*/
static compareVersions(a, b) {
for (let i = 0; i < 3; i++) {
const aVal = a[i] ?? 0;
const bVal = b[i] ?? 0;
if (aVal < bVal)
return -1;
if (aVal > bVal)
return 1;
}
return 0;
}
/**
* Validate environment variable name
*/
static validateEnvVarName(name) {
if (!name) {
throw new types_1.ValidationError('Environment variable name is required');
}
if (!/^[A-Z][A-Z0-9_]*$/.test(name)) {
throw new types_1.ValidationError(`Environment variable name "${name}" must be uppercase letters, numbers, and underscores only`);
}
}
/**
* Sanitize and validate file size
*/
static validateFileSize(size, maxSize, filename) {
if (size < 0) {
throw new types_1.ValidationError(`Invalid file size for ${filename}: ${size}`);
}
if (size > maxSize) {
const sizeMB = Math.round(size / 1024 / 1024);
const maxSizeMB = Math.round(maxSize / 1024 / 1024);
throw new types_1.ValidationError(`File ${filename} is too large: ${sizeMB}MB (max: ${maxSizeMB}MB)`);
}
}
/**
* Validate timeout value
*/
static validateTimeout(timeout) {
if (timeout <= 0) {
throw new types_1.ValidationError('Timeout must be greater than 0');
}
if (timeout > 300000) { // 5 minutes
console.warn(`⚠️ Large timeout value: ${timeout}ms (${timeout / 1000}s)`);
}
}
/**
* Validate retry count
*/
static validateRetryCount(retries) {
if (retries < 0) {
throw new types_1.ValidationError('Retry count cannot be negative');
}
if (retries > 10) {
console.warn(`⚠️ High retry count: ${retries} (this may cause long delays)`);
}
}
}
exports.ValidationUtils = ValidationUtils;
//# sourceMappingURL=validation.js.map