UNPKG

@synet/patterns

Version:

Robust, battle-tested collection of stable patterns used in Synet packages

391 lines (390 loc) 12.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Guard = void 0; const result_1 = require("./result"); /** * Guard provides type-specific validation methods organized by data type. * * This design improves discoverability and organization of validation logic. * * */ class Guard { /** * Common validation for any type */ static defined(value, name) { if (value === null || value === undefined) { return result_1.Result.fail(`${name} cannot be null or undefined`); } return result_1.Result.success(value); } /** * Validates that a condition is true */ static assert(condition, message) { if (!condition) { return result_1.Result.fail(message); } return result_1.Result.success(undefined); } /** * Combines multiple validation results into a single result */ static combine(results) { const failures = results.filter((result) => result.isFailure); if (failures.length === 0) { return result_1.Result.success(undefined); } const errorMessages = failures .map((result) => result.errorMessage) .filter((message) => !!message) .join("; "); return result_1.Result.fail(errorMessages); } /** * Validates that a boolean is true. Non-static */ isTrue(value, name) { const definedResult = Guard.defined(value, name); if (definedResult.isFailure) { return definedResult; } if (value !== true) { return result_1.Result.fail(`${name} must be true`); } return result_1.Result.success(value); } } exports.Guard = Guard; /** * String-specific validations */ Guard.String = { /** * Validates that a string is not empty */ nonEmpty(value, name) { const definedResult = Guard.defined(value, name); if (definedResult.isFailure) { return definedResult; } if (value.trim().length === 0) { return result_1.Result.fail(`${name} cannot be empty`); } return result_1.Result.success(value); }, /** * Validates that a string matches a specific pattern */ pattern(value, regex, name, message) { const definedResult = Guard.defined(value, name); if (definedResult.isFailure) { return definedResult; } if (!regex.test(value)) { return result_1.Result.fail(message || `${name} has an invalid format`); } return result_1.Result.success(value); }, /** * Validates that a string is a valid email */ email(value, name) { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return Guard.String.pattern(value, emailRegex, name, `${name} must be a valid email address`); }, /** * Validates that a string has a minimum length */ minLength(value, minLength, name) { const definedResult = Guard.defined(value, name); if (definedResult.isFailure) { return definedResult; } if (value.length < minLength) { return result_1.Result.fail(`${name} must be at least ${minLength} characters long`); } return result_1.Result.success(value); }, /** * Validates that a string doesn't exceed maximum length */ maxLength(value, maxLength, name) { const definedResult = Guard.defined(value, name); if (definedResult.isFailure) { return definedResult; } if (value.length > maxLength) { return result_1.Result.fail(`${name} must not exceed ${maxLength} characters`); } return result_1.Result.success(value); }, /** * Validates that a string's length is within a range */ length(value, minLength, maxLength, name) { const minResult = Guard.String.minLength(value, minLength, name); if (minResult.isFailure) { return minResult; } return Guard.String.maxLength(value, maxLength, name); }, /** * Validates that a string contains only letters */ letters(value, name) { return Guard.String.pattern(value, /^[a-zA-Z]+$/, name, `${name} must contain only letters`); }, /** * Validates that a string contains only alphanumeric characters */ alphanumeric(value, name) { return Guard.String.pattern(value, /^[a-zA-Z0-9]+$/, name, `${name} must contain only alphanumeric characters`); }, }; /** * Number-specific validations */ Guard.Number = { /** * Validates that a number is positive (greater than 0) */ positive(value, name) { const definedResult = Guard.defined(value, name); if (definedResult.isFailure) { return definedResult; } if (value <= 0) { return result_1.Result.fail(`${name} must be positive`); } return result_1.Result.success(value); }, /** * Validates that a number is non-negative (0 or greater) */ nonNegative(value, name) { const definedResult = Guard.defined(value, name); if (definedResult.isFailure) { return definedResult; } if (value < 0) { return result_1.Result.fail(`${name} cannot be negative`); } return result_1.Result.success(value); }, /** * Validates that a number is within a range */ range(value, min, max, name) { const definedResult = Guard.defined(value, name); if (definedResult.isFailure) { return definedResult; } if (value < min || value > max) { return result_1.Result.fail(`${name} must be between ${min} and ${max}`); } return result_1.Result.success(value); }, /** * Validates that a number is greater than a minimum value */ min(value, min, name) { const definedResult = Guard.defined(value, name); if (definedResult.isFailure) { return definedResult; } if (value < min) { return result_1.Result.fail(`${name} must be at least ${min}`); } return result_1.Result.success(value); }, /** * Validates that a number is less than a maximum value */ max(value, max, name) { const definedResult = Guard.defined(value, name); if (definedResult.isFailure) { return definedResult; } if (value > max) { return result_1.Result.fail(`${name} must not exceed ${max}`); } return result_1.Result.success(value); }, /** * Validates that a number is an integer */ integer(value, name) { const definedResult = Guard.defined(value, name); if (definedResult.isFailure) { return definedResult; } if (!Number.isInteger(value)) { return result_1.Result.fail(`${name} must be an integer`); } return result_1.Result.success(value); }, }; /** * Array-specific validations */ Guard.Array = { /** * Validates that an array is not empty */ nonEmpty(array, name) { const definedResult = Guard.defined(array, name); if (definedResult.isFailure) { return definedResult; } if (array.length === 0) { return result_1.Result.fail(`${name} cannot be empty`); } return result_1.Result.success(array); }, /** * Validates that an array has a minimum length */ minLength(array, minLength, name) { const definedResult = Guard.defined(array, name); if (definedResult.isFailure) { return definedResult; } if (array.length < minLength) { return result_1.Result.fail(`${name} must contain at least ${minLength} items`); } return result_1.Result.success(array); }, /** * Validates that an array doesn't exceed maximum length */ maxLength(array, maxLength, name) { const definedResult = Guard.defined(array, name); if (definedResult.isFailure) { return definedResult; } if (array.length > maxLength) { return result_1.Result.fail(`${name} must not contain more than ${maxLength} items`); } return result_1.Result.success(array); }, /** * Validates that an array contains a specific item */ includes(array, item, name) { const definedResult = Guard.defined(array, name); if (definedResult.isFailure) { return definedResult; } if (!array.includes(item)) { return result_1.Result.fail(`${name} does not contain the required item`); } return result_1.Result.success(array); }, /** * Validates that all items in an array meet a condition */ every(array, predicate, name, message) { const definedResult = Guard.defined(array, name); if (definedResult.isFailure) { return definedResult; } if (!array.every(predicate)) { return result_1.Result.fail(message); } return result_1.Result.success(array); }, }; /** * Object-specific validations */ Guard.Object = { /** * Validates that an object has a specific property */ hasProperty(obj, property, name) { const definedResult = Guard.defined(obj, name); if (definedResult.isFailure) { return definedResult; } if (!(property in obj)) { return result_1.Result.fail(`${name} does not have the required property '${String(property)}'`); } // This cast is safe because we've verified the property exists return result_1.Result.success(obj); }, /** * Validates that an object is not empty */ nonEmpty(obj, name) { const definedResult = Guard.defined(obj, name); if (definedResult.isFailure) { return definedResult; } if (Object.keys(obj).length === 0) { return result_1.Result.fail(`${name} cannot be empty`); } return result_1.Result.success(obj); }, }; /** * Date-specific validations */ Guard.Date = { /** * Validates that a date is in the past */ inPast(date, name) { const definedResult = Guard.defined(date, name); if (definedResult.isFailure) { return definedResult; } const now = new Date(); if (date >= now) { return result_1.Result.fail(`${name} must be in the past`); } return result_1.Result.success(date); }, /** * Validates that a date is in the future */ inFuture(date, name) { const definedResult = Guard.defined(date, name); if (definedResult.isFailure) { return definedResult; } const now = new Date(); if (date <= now) { return result_1.Result.fail(`${name} must be in the future`); } return result_1.Result.success(date); }, /** * Validates that a date is after a specific date */ after(date, threshold, name) { const definedResult = Guard.defined(date, name); if (definedResult.isFailure) { return definedResult; } if (date <= threshold) { return result_1.Result.fail(`${name} must be after ${threshold.toISOString()}`); } return result_1.Result.success(date); }, /** * Validates that a date is before a specific date */ before(date, threshold, name) { const definedResult = Guard.defined(date, name); if (definedResult.isFailure) { return definedResult; } if (date >= threshold) { return result_1.Result.fail(`${name} must be before ${threshold.toISOString()}`); } return result_1.Result.success(date); }, }; ;