@synet/patterns
Version:
Robust, battle-tested collection of stable patterns used in Synet packages
391 lines (390 loc) • 12.1 kB
JavaScript
;
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);
},
};
;