UNPKG

n4s

Version:

typed schema validation version of enforce

1,737 lines (1,666 loc) 76.9 kB
//#region rolldown:runtime var __defProp = Object.defineProperty; var __export = (all, symbols) => { let target = {}; for (var name in all) { __defProp(target, name, { get: all[name], enumerable: true }); } if (symbols) { __defProp(target, Symbol.toStringTag, { value: "Module" }); } return target; }; //#endregion let vest_utils = require("vest-utils"); let context = require("context"); //#region src/enforceContext.ts /** * Context API for accessing validation state during rule execution. * Provides access to the current value being validated, metadata, and parent context. * Used internally by rules to track nested validation (e.g., in shape, isArrayOf). * * @example * ```typescript * // Access context in custom rules * enforce.extend({ * customRule: (value: any) => { * const context = enforce.context(); * console.log('Current value:', context?.value); * console.log('Metadata:', context?.meta); * return true; * } * }); * * // Context is automatically set in nested validations * enforce({ user: { name: 'John' } }).shape({ * user: enforce.shape({ * name: enforce.isString() * }) * }); * // When validating 'name', context.parent() gives access to 'user' object * ``` */ const ctx = (0, context.createCascade)((ctxRef, parentContext) => { const base = { value: ctxRef.value, meta: ctxRef.meta || {} }; if (!parentContext) return (0, vest_utils.assign)(base, { parent: emptyParent }); else if (ctxRef.set) return (0, vest_utils.assign)(base, { parent: () => stripContext(parentContext) }); return parentContext; }); function stripContext(ctx$1) { return { value: ctx$1.value, meta: ctx$1.meta, parent: ctx$1.parent }; } function emptyParent() { return null; } //#endregion //#region src/rules/boolean/isBoolean.ts /** * Validates that a value is a boolean. * Type guard that narrows the type to boolean. * * @param value - Value to validate * @returns True if value is a boolean * * @example * ```typescript * // Eager API * enforce(true).isBoolean(); // passes * enforce(false).isBoolean(); // passes * enforce(1).isBoolean(); // fails * enforce('true').isBoolean(); // fails * * // Lazy API * const boolRule = enforce.isBoolean(); * boolRule.test(true); // true * boolRule.test(0); // false * * // Chains with boolean-specific rules * enforce(true).isBoolean().isTrue(); * ``` */ function isBoolean(value) { return (0, vest_utils.isBoolean)(value); } //#endregion //#region src/rules/boolean/isFalse.ts function isFalse(value) { return value === false; } //#endregion //#region src/rules/boolean/isTrue.ts function isTrue(value) { return value === true; } //#endregion //#region src/rules/general/equals.ts function equals$1(value, v) { return value === v; } //#endregion //#region src/rules/general/isFalsy.ts function isFalsy(value) { return !value; } //#endregion //#region src/rules/general/isTruthy.ts function isTruthy(value) { return !!value; } //#endregion //#region src/rules/booleanRules.ts var booleanRules_exports = /* @__PURE__ */ __export({ equals: () => equals$1, isBoolean: () => isBoolean, isFalse: () => isFalse, isFalsy: () => isFalsy, isTrue: () => isTrue, isTruthy: () => isTruthy }); //#endregion //#region src/ruleResult.ts function enforceMessage(ruleName, transformedResult, value, customMessage) { if (!(0, vest_utils.isNullish)(customMessage)) return (0, vest_utils.StringObject)(customMessage); if ((0, vest_utils.isNullish)(transformedResult.message)) return `enforce/${ruleName} failed with ${JSON.stringify(value)}`; return (0, vest_utils.StringObject)(transformedResult.message); } function transformResult(result, ruleName, value, ...args) { validateResult(result); if (isBoolean(result)) return { pass: result }; return { pass: !!result.pass, message: (0, vest_utils.dynamicValue)(result.message, ruleName, value, ...args), path: result.path }; } function validateResult(result) { (0, vest_utils.invariant)(isBoolean(result) || result && isBoolean(result.pass), "Incorrect return value for rule: " + JSON.stringify(result)); } //#endregion //#region src/eager/ruleCallGenerator.ts function createRuleCall(config) { const { target, rule, ruleName, value, customMessage, clearMessage } = config; return function ruleCall(...args) { const transformedResult = ctx.run({ value }, () => transformResult(rule(value, ...args), ruleName, value, ...args)); (0, vest_utils.invariant)(transformedResult.pass, enforceMessage(ruleName, transformedResult, value, customMessage)); clearMessage(); target.pass = transformedResult.pass; return target; }; } //#endregion //#region src/rules/array/includes.ts function includes(arr, item) { return Array.isArray(arr) && arr.includes(item); } //#endregion //#region src/rules/array/isArrayRule.ts /** * Validates that a value is an array. * Type guard that narrows the type to any[]. * * @param value - Value to validate * @returns True if value is an array * * @example * ```typescript * // Eager API * enforce([1, 2, 3]).isArray(); // passes * enforce('hello').isArray(); // fails * * // Lazy API * const arrayRule = enforce.isArray(); * arrayRule.test([1, 2]); // true * arrayRule.test({}); // false * ``` */ function isArray(value) { return Array.isArray(value); } //#endregion //#region src/rules/commonComparison.ts var commonComparison_exports = /* @__PURE__ */ __export({ equals: () => equals, greaterThan: () => greaterThan$4, greaterThanOrEquals: () => greaterThanOrEquals$1, lessThan: () => lessThan$1, lessThanOrEquals: () => lessThanOrEquals$1, notEquals: () => notEquals }); /** * Common comparison predicates that work across multiple types */ function equals(a, b) { return a === b; } function notEquals(a, b) { return a !== b; } function greaterThan$4(a, b) { return a > b; } function greaterThanOrEquals$1(a, b) { return a >= b; } function lessThan$1(a, b) { return a < b; } function lessThanOrEquals$1(a, b) { return a <= b; } //#endregion //#region src/rules/commonContainer.ts var commonContainer_exports = /* @__PURE__ */ __export({ inside: () => inside, notInside: () => notInside }); /** * Common container predicates for arrays and strings */ function isStringContainment(value, container) { return typeof container === "string" && typeof value === "string"; } function checkAllItemsInSet(items, set) { for (const item of items) if (!set.has(item)) return false; return true; } function checkAnyItemNotInSet(items, set) { for (const item of items) if (!set.has(item)) return true; return false; } function inside(value, container) { if (isStringContainment(value, container)) return container.includes(value); if (Array.isArray(container)) { const set = new Set(container); return Array.isArray(value) ? checkAllItemsInSet(value, set) : set.has(value); } return false; } function notInside(value, container) { if (isStringContainment(value, container)) return !container.includes(value); if (Array.isArray(container)) { const set = new Set(container); return Array.isArray(value) ? checkAnyItemNotInSet(value, set) : !set.has(value); } return true; } //#endregion //#region src/rules/commonLength.ts var commonLength_exports = /* @__PURE__ */ __export({ lengthEquals: () => lengthEquals, lengthNotEquals: () => lengthNotEquals, longerThan: () => longerThan, longerThanOrEquals: () => longerThanOrEquals, max: () => max, maxLength: () => maxLength, min: () => min, minLength: () => minLength, shorterThan: () => shorterThan, shorterThanOrEquals: () => shorterThanOrEquals }); function minLength(value, n) { return value.length >= n; } function maxLength(value, n) { return value.length <= n; } const min = minLength; const max = maxLength; function lengthEquals(value, n) { return value.length === n; } function lengthNotEquals(value, n) { return value.length !== n; } function longerThan(value, n) { return value.length > n; } function longerThanOrEquals(value, n) { return value.length >= n; } function shorterThan(value, n) { return value.length < n; } function shorterThanOrEquals(value, n) { return value.length <= n; } //#endregion //#region src/rules/general/isNotArray.ts /** * Validates that a value is not an array. * Inverse of isArray. * * @param value - Value to validate * @returns True if value is not an array * * @example * ```typescript * enforce({}).isNotArray(); // passes * enforce('hello').isNotArray(); // passes * enforce([1, 2, 3]).isNotArray(); // fails * ``` */ function isNotArray(value) { return !Array.isArray(value); } //#endregion //#region src/rules/arrayRules.ts var arrayRules_exports = /* @__PURE__ */ __export({ equals: () => equals, includes: () => includes, inside: () => inside, isArray: () => isArray, isEmpty: () => vest_utils.isEmpty, isNotArray: () => isNotArray, isNotEmpty: () => vest_utils.isNotEmpty, lengthEquals: () => lengthEquals, lengthNotEquals: () => lengthNotEquals, longerThan: () => longerThan, longerThanOrEquals: () => longerThanOrEquals, maxLength: () => maxLength, minLength: () => minLength, notEquals: () => notEquals, notInside: () => notInside, shorterThan: () => shorterThan, shorterThanOrEquals: () => shorterThanOrEquals }); //#endregion //#region src/utils/RuleRunReturn.ts /** * Represents the result of a validation rule execution. * Contains the pass/fail status, the validated type, and an optional error message. * * @template T - The type of value that was validated * * @example * ```typescript * const result = RuleRunReturn.Passing('hello'); * console.log(result.pass); // true * console.log(result.type); // 'hello' * * const failed = RuleRunReturn.Failing(123, 'Must be positive'); * console.log(failed.pass); // false * console.log(failed.message); // 'Must be positive' * ``` */ var RuleRunReturn = class RuleRunReturn { constructor(pass, type, message) { this.pass = pass; this.type = type; this.message = message; } /** * Creates a RuleRunReturn from a boolean or existing RuleRunReturn. * Handles message resolution and type coercion. * * @param pass - Boolean indicating success, or existing RuleRunReturn * @param type - The type of the validated value * @param message - Optional error message (can be string or function) * @returns A new RuleRunReturn instance */ static create(pass, type, message) { if ((0, vest_utils.isBoolean)(pass)) return new RuleRunReturn(!!pass, type, (0, vest_utils.dynamicValue)(message, type)); return RuleRunReturn.fromObject(pass, type, message); } static fromObject(pass, type, message) { if (!(pass && (0, vest_utils.isBoolean)(pass.pass))) return new RuleRunReturn(false, type, (0, vest_utils.dynamicValue)(message, type)); const resolvedPass = !!pass.pass; const successType = pass.type === void 0 ? type : pass.type; const failureType = type === void 0 ? pass.type : type; const res = new RuleRunReturn(resolvedPass, resolvedPass ? successType : failureType, (0, vest_utils.dynamicValue)(message ?? pass.message, type)); res.path = pass.path; return res; } /** * Creates a passing RuleRunReturn. * * @param type - The validated value's type * @param message - Optional success message * @returns A RuleRunReturn with pass=true * * @example * ```typescript * const result = RuleRunReturn.Passing('valid'); * console.log(result.pass); // true * ``` */ static Passing(type, message) { return RuleRunReturn.create(true, type, message); } /** * Creates a failing RuleRunReturn. * * @param type - The validated value's type * @param message - Optional error message * @returns A RuleRunReturn with pass=false * * @example * ```typescript * const result = RuleRunReturn.Failing(123, 'Number must be positive'); * console.log(result.pass); // false * console.log(result.message); // 'Number must be positive' * ``` */ static Failing(type, message) { return RuleRunReturn.create(false, type, message); } }; //#endregion //#region src/rules/compoundRules/allOf.ts /** * Validates that a value passes all of the provided rules. * All rules must pass for the validation to succeed. * Evaluation stops at the first failing rule. * * @template T - The value type to validate * @param value - The value to validate * @param rules - One or more RuleInstances that must all pass * @returns RuleRunReturn indicating success or failure * * @example * ```typescript * // Eager API * enforce(25) * .allOf( * enforce.isNumber().greaterThan(18).lessThan(100) * ); // passes (all rules pass) * * // Lazy API * const adultAgeRule = enforce.allOf( * enforce.isNumber().greaterThanOrEquals(18).lessThan(150) * ); * * adultAgeRule.test(25); // true * adultAgeRule.test(16); // false * adultAgeRule.test('25'); // false (not a number) * ``` */ function allOf(value, ...rules) { return (0, vest_utils.mapFirst)(rules, (rule, breakout) => { const res = rule.run(value); breakout(!res.pass, res); }) || RuleRunReturn.Passing(value); } //#endregion //#region src/rules/compoundRules/anyOf.ts /** * Validates that a value passes at least one of the provided rules. * At least one rule must pass for the validation to succeed. * Evaluation stops at the first passing rule. * * @template T - The value type to validate * @param value - The value to validate * @param rules - One or more RuleInstances where at least one must pass * @returns RuleRunReturn indicating success or failure * * @example * ```typescript * // Eager API * enforce('hello') * .anyOf( * enforce.isNumber(), * enforce.isString() * ); // passes (string matches) * * // Lazy API - accept either format * const phoneOrEmailRule = enforce.anyOf( * enforce.isString().matches(/^\d{10}$/), // phone * enforce.isString().matches(/@/) // email * ); * * phoneOrEmailRule.test('1234567890'); // true * phoneOrEmailRule.test('user@example.com'); // true * phoneOrEmailRule.test('invalid'); // false * ``` */ function anyOf(value, ...rules) { return (0, vest_utils.mapFirst)(rules, (rule, breakout) => { const res = rule.run(value); breakout(res.pass, res); }) || RuleRunReturn.Failing(value); } //#endregion //#region src/rules/compoundRules/noneOf.ts /** * Validates that a value passes none of the provided rules. * All rules must fail for the validation to succeed. * Evaluation stops at the first passing rule. * * @template T - The value type to validate * @param value - The value to validate * @param rules - One or more RuleInstances that must all fail * @returns RuleRunReturn indicating success or failure * * @example * ```typescript * // Eager API * enforce(0) * .noneOf( * enforce.greaterThan(0), * enforce.lessThan(0) * ); // passes (neither rule passes) * * // Lazy API - exclude reserved usernames * const notReservedRule = enforce.noneOf( * enforce.equals('admin'), * enforce.equals('root'), * enforce.equals('system') * ); * * notReservedRule.test('john'); // true * notReservedRule.test('admin'); // false * notReservedRule.test('root'); // false * ``` */ function noneOf(value, ...rules) { return (0, vest_utils.mapFirst)(rules, (rule, breakout) => { breakout(rule.run(value).pass, RuleRunReturn.Failing(value)); }) || RuleRunReturn.Passing(value); } //#endregion //#region src/rules/compoundRules/oneOf.ts const REQUIRED_COUNT = 1; /** * Validates that a value passes exactly one of the provided rules. * Exactly one rule must pass - not zero, not two or more. * All rules are evaluated to count passing rules. * * @template T - The value type to validate * @param value - The value to validate * @param rules - One or more RuleInstances where exactly one must pass * @returns RuleRunReturn indicating success or failure * * @example * ```typescript * // Eager API * enforce(5) * .oneOf( * enforce.lessThan(10), * enforce.greaterThan(100) * ); // passes (only first rule passes) * * // Lazy API - accept either type but not both * const stringOrNumberRule = enforce.oneOf( * enforce.isString(), * enforce.isNumber() * ); * * stringOrNumberRule.test('hello'); // true * stringOrNumberRule.test(42); // true * stringOrNumberRule.test(true); // false (no rules pass) * * // More complex example - exclusive validation * const exclusiveRule = enforce.oneOf( * enforce.equals(null), * enforce.isString().longerThan(0) * ); * * exclusiveRule.test(null); // true (exactly one passes) * exclusiveRule.test('hello'); // true (exactly one passes) * exclusiveRule.test(''); // false (neither passes) * ``` */ function oneOf(value, ...rules) { let passingCount = 0; rules.some((rule) => { if (rule.run(value).pass) passingCount++; if ((0, vest_utils.greaterThan)(passingCount, REQUIRED_COUNT)) return RuleRunReturn.Failing(value); }); return RuleRunReturn.create(passingCount === REQUIRED_COUNT, value); } //#endregion //#region src/rules/compoundRules/compoundRules.ts var compoundRules_exports = /* @__PURE__ */ __export({ allOf: () => allOf, anyOf: () => anyOf, noneOf: () => noneOf, oneOf: () => oneOf }); //#endregion //#region src/rules/general/condition.ts function condition(value, callback) { try { return callback(value); } catch { return false; } } //#endregion //#region src/rules/general/notEquals.ts function notEquals$1(value, v) { return value !== v; } //#endregion //#region src/rules/general/isEmpty.ts function isEmpty(value) { return (0, vest_utils.isEmpty)(value); } //#endregion //#region src/rules/general/isNaN.ts function isNaN(value) { return Number.isNaN((0, vest_utils.toNumber)(value).unwrapOr(NaN)); } //#endregion //#region src/rules/general/isNotBoolean.ts /** * Validates that a value is not a boolean. * Inverse of isBoolean. * * @param value - Value to validate * @returns True if value is not a boolean * * @example * ```typescript * enforce(1).isNotBoolean(); // passes * enforce('true').isNotBoolean(); // passes * enforce(true).isNotBoolean(); // fails * enforce(false).isNotBoolean(); // fails * ``` */ function isNotBoolean(value) { return typeof value !== "boolean"; } //#endregion //#region src/rules/general/isNotEmpty.ts function isNotEmpty(value) { return (0, vest_utils.isNotEmpty)(value); } //#endregion //#region src/rules/general/isNotNaN.ts function isNotNaN(value) { return !Number.isNaN((0, vest_utils.toNumber)(value).unwrapOr(value)); } //#endregion //#region src/rules/general/isNotNumber.ts /** * Validates that a value is not a number (or is NaN). * Inverse of isNumber. Considers NaN as not a number. * * @param value - Value to validate * @returns True if value is not a number or is NaN * * @example * ```typescript * enforce('123').isNotNumber(); // passes * enforce(NaN).isNotNumber(); // passes * enforce(true).isNotNumber(); // passes * enforce(42).isNotNumber(); // fails * ``` */ function isNotNumber(value) { return typeof value !== "number" || Number.isNaN(value); } //#endregion //#region src/rules/general/isNotNumeric.ts /** * Validates that a value is not numeric (not a number or numeric string). * Inverse of isNumeric. * * @param value - Value to validate * @returns True if value is not numeric * * @example * ```typescript * enforce('hello').isNotNumeric(); // passes * enforce(true).isNotNumeric(); // passes * enforce(NaN).isNotNumeric(); // passes * enforce(42).isNotNumeric(); // fails * enforce('42').isNotNumeric(); // fails * ``` */ function isNotNumeric(value) { if (typeof value === "number") return Number.isNaN(value); return !(0, vest_utils.isNumeric)(value); } //#endregion //#region src/rules/general/isNotString.ts /** * Validates that a value is not a string. * Inverse of isString. * * @param value - Value to validate * @returns True if value is not a string * * @example * ```typescript * enforce(123).isNotString(); // passes * enforce([]).isNotString(); // passes * enforce('hello').isNotString(); // fails * ``` */ function isNotString(value) { return typeof value !== "string"; } //#endregion //#region src/rules/general/isNotNull.ts /** * Validates that a value is not null. * Inverse of isNull. Note: undefined passes this check. * * @param value - Value to validate * @returns True if value is not null * * @example * ```typescript * enforce(undefined).isNotNull(); // passes * enforce(0).isNotNull(); // passes * enforce('').isNotNull(); // passes * enforce(null).isNotNull(); // fails * ``` */ function isNotNull(value) { return (0, vest_utils.isNotNull)(value); } //#endregion //#region src/rules/general/isNotUndefined.ts /** * Validates that a value is not undefined. * Inverse of isUndefined. Note: null passes this check. * * @param value - Value to validate * @returns True if value is not undefined * * @example * ```typescript * enforce(null).isNotUndefined(); // passes * enforce(0).isNotUndefined(); // passes * enforce('').isNotUndefined(); // passes * enforce(undefined).isNotUndefined(); // fails * ``` */ function isNotUndefined(value) { return (0, vest_utils.isNotUndefined)(value); } //#endregion //#region src/rules/general/isNotNullish.ts /** * Validates that a value is not nullish (not null and not undefined). * Inverse of isNullish. * * @param value - Value to validate * @returns True if value is neither null nor undefined * * @example * ```typescript * enforce(0).isNotNullish(); // passes * enforce('').isNotNullish(); // passes * enforce(false).isNotNullish(); // passes * enforce(null).isNotNullish(); // fails * enforce(undefined).isNotNullish(); // fails * ``` */ function isNotNullish(value) { return (0, vest_utils.isNotNullish)(value); } //#endregion //#region src/rules/general/isBlank.ts function isBlank(value) { return (0, vest_utils.isNullish)(value) || (0, vest_utils.isStringValue)(value) && !value.trim(); } function isNotBlank$1(value) { return !isBlank(value); } //#endregion //#region src/rules/generalRules.ts var generalRules_exports = /* @__PURE__ */ __export({ condition: () => condition, equals: () => equals$1, isBlank: () => isBlank, isEmpty: () => isEmpty, isFalsy: () => isFalsy, isNaN: () => isNaN, isNotArray: () => isNotArray, isNotBlank: () => isNotBlank$1, isNotBoolean: () => isNotBoolean, isNotEmpty: () => isNotEmpty, isNotNaN: () => isNotNaN, isNotNull: () => isNotNull, isNotNullish: () => isNotNullish, isNotNumber: () => isNotNumber, isNotNumeric: () => isNotNumeric, isNotString: () => isNotString, isNotUndefined: () => isNotUndefined, isTruthy: () => isTruthy, notEquals: () => notEquals$1 }); //#endregion //#region src/rules/nullish/isNull.ts /** * Validates that a value is null. * Type guard that narrows the type to null. * * @param value - Value to validate * @returns True if value is null * * @example * ```typescript * // Eager API * enforce(null).isNull(); // passes * enforce(undefined).isNull(); // fails * enforce(0).isNull(); // fails * * // Lazy API * const nullRule = enforce.isNull(); * nullRule.test(null); // true * nullRule.test(undefined); // false * ``` */ function isNull(value) { return (0, vest_utils.isNull)(value); } //#endregion //#region src/rules/nullish/isUndefined.ts /** * Validates that a value is undefined. * Type guard that narrows the type to undefined. * * @param value - Value to validate * @returns True if value is undefined * * @example * ```typescript * // Eager API * enforce(undefined).isUndefined(); // passes * enforce(null).isUndefined(); // fails * enforce('').isUndefined(); // fails * * // Lazy API * const undefRule = enforce.isUndefined(); * undefRule.test(undefined); // true * undefRule.test(null); // false * * // Useful for optional properties * const schema = enforce.shape({ * optional: enforce.optional(enforce.isString()) * }); * ``` */ function isUndefined(value) { return (0, vest_utils.isUndefined)(value); } //#endregion //#region src/rules/nullish/isNullish.ts /** * Validates that a value is null or undefined (nullish). * Type guard that narrows the type to null | undefined. * * @param value - Value to validate * @returns True if value is null or undefined * * @example * ```typescript * // Eager API * enforce(null).isNullish(); // passes * enforce(undefined).isNullish(); // passes * enforce(0).isNullish(); // fails * enforce('').isNullish(); // fails * enforce(false).isNullish(); // fails * * // Lazy API * const nullishRule = enforce.isNullish(); * nullishRule.test(null); // true * nullishRule.test(undefined); // true * nullishRule.test(0); // false * ``` */ function isNullish(value) { return (0, vest_utils.isNullish)(value); } //#endregion //#region src/rules/nullishRules.ts var nullishRules_exports = /* @__PURE__ */ __export({ isNull: () => isNull, isNullish: () => isNullish, isUndefined: () => isUndefined }); //#endregion //#region src/rules/number/greaterThanOrEquals.ts function greaterThanOrEquals(value, gte$1) { return (0, vest_utils.numberEquals)(value, gte$1) || (0, vest_utils.greaterThan)(value, gte$1); } //#endregion //#region src/rules/number/lessThan.ts function lessThan(value, lt$1) { return (0, vest_utils.isNumeric)(value) && (0, vest_utils.isNumeric)(lt$1) && Number(value) < Number(lt$1); } //#endregion //#region src/rules/number/lessThanOrEquals.ts function lessThanOrEquals(value, lte$1) { return (0, vest_utils.numberEquals)(value, lte$1) || lessThan(value, lte$1); } //#endregion //#region src/rules/number/isBetween.ts function isBetween(value, min$1, max$1) { return greaterThanOrEquals(value, min$1) && lessThanOrEquals(value, max$1); } const isNotBetween$1 = (0, vest_utils.bindNot)(isBetween); //#endregion //#region src/rules/number/isEven.ts /** * Validates that a given value is an even number */ const isEven = (value) => { if ((0, vest_utils.isNumeric)(value)) { const asNumber = (0, vest_utils.toNumber)(value).unwrap(); if (asNumber !== null) return asNumber % 2 === 0; } return false; }; //#endregion //#region src/rules/number/isNegative.ts function isNegative(value) { return value < 0; } //#endregion //#region src/rules/number/isNotBetween.ts function isNotBetween(value, min$1, max$1) { return value < min$1 || value > max$1; } //#endregion //#region src/rules/number/isNumber.ts /** * Validates that a value is a number (excluding NaN). * Type guard that narrows the type to number. * * @param value - Value to validate * @returns True if value is a number and not NaN * * @example * ```typescript * // Eager API * enforce(42).isNumber(); // passes * enforce('42').isNumber(); // fails (string) * enforce(NaN).isNumber(); // fails (NaN is excluded) * * // Lazy API * const numberRule = enforce.isNumber(); * numberRule.test(42); // true * numberRule.test(Infinity); // true * numberRule.test(NaN); // false * * // Chains with number-specific rules * enforce(25).isNumber().greaterThan(18); * ``` */ function isNumber(value) { return typeof value === "number" && !Number.isNaN(value); } //#endregion //#region src/rules/number/isOdd.ts /** * Validates that a given value is an odd number */ const isOdd = (value) => { if ((0, vest_utils.isNumeric)(value)) { const asNumber = (0, vest_utils.toNumber)(value).unwrap(); if (asNumber !== null) return asNumber % 2 !== 0; } return false; }; //#endregion //#region src/rules/number/isPositive.ts function isPositive(value) { return value > 0; } //#endregion //#region src/rules/numeric/toNumber.ts function toNumber(value) { const result = (0, vest_utils.toNumber)(value); if ((0, vest_utils.isFailure)(result)) return RuleRunReturn.Failing(NaN, result.error); return RuleRunReturn.Passing(result.value); } //#endregion //#region src/rules/number/numberNotEquals.ts function numberNotEquals(value, n) { return (0, vest_utils.numberNotEquals)(value, n); } //#endregion //#region src/rules/numberRules.ts var numberRules_exports = /* @__PURE__ */ __export({ eq: () => eq, equals: () => equals$1, greaterThan: () => vest_utils.greaterThan, greaterThanOrEquals: () => greaterThanOrEquals, gt: () => gt, gte: () => gte, isBetween: () => isBetween, isEven: () => isEven, isNaN: () => isNaN, isNegative: () => isNegative, isNotBetween: () => isNotBetween, isNotNaN: () => isNotNaN, isNumber: () => isNumber, isOdd: () => isOdd, isPositive: () => isPositive, lessThan: () => lessThan, lessThanOrEquals: () => lessThanOrEquals, lt: () => lt, lte: () => lte, neq: () => neq, numberEquals: () => vest_utils.numberEquals, numberNotEquals: () => numberNotEquals, toNumber: () => toNumber }); const gt = vest_utils.greaterThan; const gte = greaterThanOrEquals; const lt = lessThan; const lte = lessThanOrEquals; const eq = equals$1; const neq = numberNotEquals; const aliases = { eq, gt, gte, lt, lte, neq }; ({ ...aliases }); //#endregion //#region src/rules/numeric/isNumeric.ts /** * Validates that a value is numeric (a number or a numeric string). * Type guard that narrows the type to number | string. * Excludes NaN but includes Infinity and numeric strings. * * @param value - Value to validate * @returns True if value is a number or numeric string * * @example * ```typescript * // Eager API * enforce(42).isNumeric(); // passes * enforce('42').isNumeric(); // passes (numeric string) * enforce('42.5').isNumeric(); // passes * enforce(Infinity).isNumeric(); // passes * enforce('hello').isNumeric(); // fails * enforce(NaN).isNumeric(); // fails * * // Lazy API * const numericRule = enforce.isNumeric(); * numericRule.test(100); // true * numericRule.test('100'); // true * numericRule.test('abc'); // false * * // Chains with numeric comparison rules * enforce('25').isNumeric().greaterThan(18); * ``` */ function isNumeric(value) { if (typeof value === "number") return !Number.isNaN(value); return (0, vest_utils.isNumeric)(value); } //#endregion //#region src/rules/object/isKeyOf.ts function isKeyOf(key, obj) { return (0, vest_utils.isObject)(obj) && (0, vest_utils.hasOwnProperty)(obj, key); } function isNotKeyOf(key, obj) { return !(0, vest_utils.isObject)(obj) || !(0, vest_utils.hasOwnProperty)(obj, key); } //#endregion //#region src/rules/object/isValueOf.ts function isValueOf(value, obj) { return (0, vest_utils.isObject)(obj) && Object.values(obj).includes(value); } function isNotValueOf(value, obj) { return (0, vest_utils.isObject)(obj) && !Object.values(obj).includes(value); } //#endregion //#region src/rules/objectRules.ts var objectRules_exports = /* @__PURE__ */ __export({ isKeyOf: () => isKeyOf, isNotKeyOf: () => isNotKeyOf, isNotValueOf: () => isNotValueOf, isValueOf: () => isValueOf }); //#endregion //#region src/rules/schemaRules/isArrayOf.ts /** * Validates that a value is an array and all elements match at least one of the provided rules. * Each array element must pass at least one of the validation rules. * * @template T - The element type of the array * @param value - The array to validate * @param rules - One or more RuleInstances that elements should match * @returns RuleRunReturn indicating success or failure * * @example * ```typescript * // Eager API - array of strings * enforce(['a', 'b', 'c']) * .isArrayOf(enforce.isString()); // passes * * enforce([1, 2, 'three']) * .isArrayOf(enforce.isString()); // fails * * // Lazy API - array of numbers or strings * const mixedArrayRule = enforce.isArrayOf( * enforce.isNumber(), * enforce.isString() * ); * * mixedArrayRule.test([1, 'two', 3, 'four']); // true * mixedArrayRule.test([1, 2, true]); // false (boolean not allowed) * * // Complex schema validation * const usersRule = enforce.isArrayOf( * enforce.shape({ * name: enforce.isString(), * age: enforce.isNumber() * }) * ); * * usersRule.test([ * { name: 'John', age: 30 }, * { name: 'Jane', age: 25 } * ]); // true * ``` */ function isArrayOf(value, ...rules) { if (!Array.isArray(value)) return RuleRunReturn.Failing(value); const parsedArray = []; return (0, vest_utils.mapFirst)(value, (item, breakout, index) => { const res = ctx.run({ value: item, set: true, meta: { index } }, () => { let lastRes; let passingTransformedType = item; if (rules.some((rule) => { const rawResult = rule.run(item); lastRes = rawResult; const transformed = transformResult(rawResult, "isArrayOf", item); if (transformed.pass) passingTransformedType = rawResult.type ?? item; return transformed.pass; })) { parsedArray.push(passingTransformedType); return RuleRunReturn.Passing(passingTransformedType); } if ((0, vest_utils.lengthEquals)(rules, 1) && lastRes) return lastRes; return RuleRunReturn.Failing(item); }); if (!res.pass) { const currentPath = res.path || []; breakout(true, { ...res, path: [index.toString(), ...currentPath] }); } }) || RuleRunReturn.Passing(parsedArray); } //#endregion //#region src/rules/schemaRules/schemaObjectUtils.ts /** * Returns only own enumerable keys for object-like values. * * Prototype keys are never traversed which prevents inherited-key surprises. */ function ownKeys(value) { if (!(0, vest_utils.isObject)(value)) return []; return Object.keys(value); } /** * Returns the first dangerous own key if present; otherwise null. */ function findDangerousOwnKey(value) { for (const key of ownKeys(value)) if ((0, vest_utils.isUnsafeKey)(key)) return key; return null; } /** * Produces a plain shallow sanitized copy that includes only own enumerable keys * and excludes dangerous keys. Prototype and non-enumerable properties are not preserved. */ function safeShallowCopy(value) { const output = {}; for (const key of ownKeys(value)) { if ((0, vest_utils.isUnsafeKey)(key)) continue; output[key] = value[key]; } return output; } /** * Returns true if both value and schema are plain objects (not arrays). */ function isValidSchemaInput(value, schema) { return (0, vest_utils.isObject)(value) && !Array.isArray(value) && (0, vest_utils.isObject)(schema) && !Array.isArray(schema); } /** * Checks if the value or the schema contain any inherently dangerous keys natively. */ function checkDangerousKeys(value, schema) { const dangerousSchemaKey = findDangerousOwnKey(schema); if (dangerousSchemaKey) return { pass: false, path: [dangerousSchemaKey] }; const dangerousValueKey = findDangerousOwnKey(value); if (dangerousValueKey) return { pass: false, path: [dangerousValueKey] }; return null; } /** * Filters schema keys using a predicate, returning a new schema * containing only the keys for which the predicate returns true. */ function filterSchemaKeys(schema, predicate) { const filtered = {}; if (!(0, vest_utils.isObject)(schema)) return filtered; for (const key of ownKeys(schema)) if (predicate(key)) filtered[key] = schema[key]; return filtered; } //#endregion //#region src/rules/schemaRules/loose.ts /** * Validates that an object matches a schema loosely - all schema keys required, extra keys allowed. * Like shape() but permits additional properties not defined in the schema. * * @template T - The object type to validate * @param value - The object to validate * @param schema - Schema mapping keys to validation rules * @returns RuleRunReturn indicating success or failure * * @example * ```typescript * // Eager API * enforce({ name: 'John', age: 30, extra: 'allowed' }) * .loose({ * name: enforce.isString(), * age: enforce.isNumber() * }); // passes (extra key is ok) * * // Lazy API * const partialUserSchema = enforce.loose({ * name: enforce.isString(), * email: enforce.isString() * }); * * // All schema keys must be present and valid * partialUserSchema.test({ name: 'Jane', email: 'jane@example.com' }); // true * partialUserSchema.test({ name: 'Jane', email: 'jane@example.com', age: 30 }); // true (extra ok) * partialUserSchema.test({ name: 'Jane' }); // false (missing email) * ``` */ function loose(value, schema) { if (!(0, vest_utils.isObject)(value)) return RuleRunReturn.Failing(value); const dangerousSchemaKey = findDangerousOwnKey(schema); if (dangerousSchemaKey) return { ...RuleRunReturn.Failing(value), path: [dangerousSchemaKey] }; const dangerousValueKey = findDangerousOwnKey(value); if (dangerousValueKey) return { ...RuleRunReturn.Failing(value), path: [dangerousValueKey] }; const parsedValue = safeShallowCopy(value); for (const key of ownKeys(schema)) { const fieldValue = (0, vest_utils.hasOwnProperty)(value, key) ? value[key] : void 0; const res = ctx.run({ value: fieldValue, set: true, meta: { key } }, () => schema[key].run(fieldValue)); if (!res.pass) { const currentPath = res.path || []; return { ...res, path: [key, ...currentPath] }; } parsedValue[key] = res.type; } return RuleRunReturn.Passing(parsedValue); } //#endregion //#region src/rules/schemaRules/optional.ts /** * Makes a validation rule optional by allowing null or undefined values to pass. * If the value is null or undefined, validation passes without running the inner rule. * Otherwise, the inner rule is executed. * * @template T - The value type to validate * @param value - The value to validate (may be null/undefined) * @param rule - The RuleInstance to apply if value is not nullish * @returns RuleRunReturn indicating success or failure * * @example * ```typescript * // Eager API * enforce(undefined).optional(enforce.isString()); // passes * enforce(null).optional(enforce.isString()); // passes * enforce('hello').optional(enforce.isString()); // passes * enforce(123).optional(enforce.isString()); // fails * * // Lazy API - useful in schemas * const userSchema = enforce.shape({ * name: enforce.isString(), * middleName: enforce.optional(enforce.isString()), * age: enforce.isNumber() * }); * * userSchema.test({ name: 'John', age: 30 }); // true (middleName optional) * userSchema.test({ name: 'John', middleName: null, age: 30 }); // true * userSchema.test({ name: 'John', middleName: 'Q', age: 30 }); // true * userSchema.test({ name: 'John', middleName: 123, age: 30 }); // false * ``` */ function optional(value, rule) { if ((0, vest_utils.isNullish)(value)) return RuleRunReturn.Passing(value); return rule.run(value); } //#endregion //#region src/rules/schemaRules/partial.ts /** * Checks if value has any keys not present in schema. */ function getFirstExtraKey(value, schema) { for (const key of ownKeys(value)) if (!(0, vest_utils.hasOwnProperty)(schema, key)) return key; return null; } /** * Validates provided keys against their schema rules and returns parsed entries. * * Missing keys are allowed (partial validation). */ function validateProvidedKeys(value, schema) { const parsedEntries = {}; for (const key of ownKeys(schema)) if ((0, vest_utils.hasOwnProperty)(value, key)) { const fieldValue = value[key]; const res = ctx.run({ value: fieldValue, set: true, meta: { key } }, () => schema[key].run(fieldValue)); if (!res.pass) { const currentPath = res.path || []; return { ...res, path: [key, ...currentPath] }; } parsedEntries[key] = res.type; } return { parsedEntries }; } /** * partial(value, schema) validates that: * 1. value's keys are a subset of schema's keys (no extras) * 2. Zero or more keys may be present (empty object is allowed) * 3. For each provided key, the corresponding rule passes */ /** * Validates that an object partially matches a schema - schema keys are optional, no extra keys allowed. * All provided keys must exist in schema and pass their validation rules. * Missing keys are allowed (making all fields optional). * * @template T - The object type to validate * @param value - The object to validate * @param schema - Schema mapping keys to validation rules * @returns RuleRunReturn indicating success or failure * * @example * ```typescript * // Eager API * enforce({ name: 'John' }) * .partial({ * name: enforce.isString(), * age: enforce.isNumber(), * email: enforce.isString() * }); // passes (age and email are optional) * * // Lazy API * const updateSchema = enforce.partial({ * name: enforce.isString(), * email: enforce.isString().matches(/@/), * age: enforce.isNumber() * }); * * updateSchema.test({}); // true (all fields optional) * updateSchema.test({ name: 'Jane' }); // true (partial update) * updateSchema.test({ name: 'Jane', email: 'jane@example.com' }); // true * updateSchema.test({ name: 'Jane', extra: 'x' }); // false (extra key not in schema) * ``` */ function partial(value, schema) { if (!(0, vest_utils.isObject)(value)) return RuleRunReturn.Failing(value); const dangerousSchemaKey = findDangerousOwnKey(schema); if (dangerousSchemaKey) return { ...RuleRunReturn.Failing(value), path: [dangerousSchemaKey] }; const dangerousValueKey = findDangerousOwnKey(value); if (dangerousValueKey) return { ...RuleRunReturn.Failing(value), path: [dangerousValueKey] }; const extraKey = getFirstExtraKey(value, schema); if (extraKey) return { ...RuleRunReturn.Failing(value), path: [extraKey] }; const parsedValue = safeShallowCopy(value); const parsedEntriesOrFailure = validateProvidedKeys(value, schema); if ("pass" in parsedEntriesOrFailure) return parsedEntriesOrFailure; return RuleRunReturn.Passing({ ...parsedValue, ...parsedEntriesOrFailure.parsedEntries }); } //#endregion //#region src/rules/schemaRules/pick.ts /** * Validates that an object loosely matches a schema but only validates the specified keys. * Other keys in the object are ignored and no validation is applied to them. * * @template T - The object type to validate * @param value - The object to validate * @param schema - Schema mapping keys to validation rules * @param keysToPick - Array of keys that should be validated from the schema * @returns RuleRunReturn indicating success or failure */ function pick(value, schema, keysToPick) { if (!isValidSchemaInput(value, schema)) return RuleRunReturn.Failing(value); const pickKeys = new Set((0, vest_utils.asArray)(keysToPick)); const dangerousKeyError = checkDangerousKeys(value, schema); if (dangerousKeyError) return { ...RuleRunReturn.Failing(value), ...dangerousKeyError }; const baseRes = loose(value, filterSchemaKeys(schema, (key) => pickKeys.has(key))); return baseRes.pass ? RuleRunReturn.Passing(baseRes.type) : baseRes; } //#endregion //#region src/rules/schemaRules/omit.ts /** * Validates that an object loosely matches a schema but omits specified keys from validation. * The omitted keys in the object are ignored and no validation is applied to them. * * @template T - The object type to validate * @param value - The object to validate * @param schema - Schema mapping keys to validation rules * @param keysToOmit - Array of keys that should be omitted from schema validation * @returns RuleRunReturn indicating success or failure */ function omit(value, schema, keysToOmit) { if (!isValidSchemaInput(value, schema)) return RuleRunReturn.Failing(value); const omitKeys = new Set((0, vest_utils.asArray)(keysToOmit)); const dangerousKeyError = checkDangerousKeys(value, schema); if (dangerousKeyError) return { ...RuleRunReturn.Failing(value), ...dangerousKeyError }; const baseRes = loose(value, filterSchemaKeys(schema, (key) => !omitKeys.has(key))); return baseRes.pass ? RuleRunReturn.Passing(baseRes.type) : baseRes; } //#endregion //#region src/rules/schemaRules/shape.ts /** * Validates that an object matches a schema exactly - all keys required, no extra keys allowed. * Each field value is validated against its corresponding RuleInstance in the schema. * * @template T - The object type to validate * @param value - The object to validate * @param schema - Schema mapping keys to validation rules * @returns RuleRunReturn indicating success or failure * * @example * ```typescript * // Eager API * enforce({ name: 'John', age: 30 }) * .shape({ * name: enforce.isString(), * age: enforce.isNumber().greaterThan(0) * }); // passes * * // Lazy API * const userSchema = enforce.shape({ * name: enforce.isString(), * email: enforce.isString().matches(/@/), * age: enforce.isNumber().greaterThanOrEquals(18) * }); * * userSchema.test({ name: 'Jane', email: 'jane@example.com', age: 25 }); // true * userSchema.test({ name: 'Jane', age: 25 }); // false (missing email) * userSchema.test({ name: 'Jane', email: 'jane@example.com', age: 25, extra: 'x' }); // false (extra key) * ``` */ function shape(value, schema) { const baseRes = loose(value, schema); if (!baseRes.pass) return baseRes; for (const key of ownKeys(value)) if (!(0, vest_utils.hasOwnProperty)(schema, key)) return { ...RuleRunReturn.Failing(value), path: [key] }; return RuleRunReturn.Passing(baseRes.type); } //#endregion //#region src/rules/schemaRules/record.ts /** * Validates that an object's dynamic keys and/or values match provided rules. * Like TypeScript's Record<K, V>, it checks elements against shape rules. * * @param value - The object to validate * @param arg1 - Either the key rule (if arg2 is present) or the value rule * @param arg2 - The value rule (if arg1 is the key rule) * @returns RuleRunReturn indicating success or failure */ function record(value, arg1, arg2) { if (!(0, vest_utils.isObject)(value) || Array.isArray(value)) return RuleRunReturn.Failing(value); const rules = parseRules(arg1, arg2); const dangerousKey = findDangerousOwnKey(value); if (dangerousKey) return createRecordFailure(value, dangerousKey, RuleRunReturn.Failing(value)); const parsedValue = safeShallowCopy(value); return (0, vest_utils.mapFirst)(ownKeys(value), (key, breakout) => { const errorRes = evaluateRecordEntry(key, value, rules, parsedValue); if (errorRes) breakout(true, errorRes); }) || RuleRunReturn.Passing(parsedValue); } function parseRules(arg1, arg2) { if (arg2 !== void 0) return { keyRule: arg1, valueRule: arg2 }; return { keyRule: void 0, valueRule: arg1 }; } function validateKey(key, keyRule) { return ctx.run({ value: key, set: true }, () => keyRule.run(key)); } function evaluateRecordEntry(key, value, rules, parsedValue) { if (rules.keyRule) { const keyRes = validateKey(key, rules.keyRule); if (!keyRes.pass) return createRecordFailure(value, key, keyRes); if (keyRes.type !== key) { delete parsedValue[key]; key = keyRes.type; } } const valRes = ctx.run({ value: value[key], set: true, meta: { key } }, () => rules.valueRule.run(value[key])); if (!valRes.pass) return createRecordFailure(value, key, valRes); parsedValue[key] = valRes.type; } function createRecordFailure(value, key, ruleRes) { const currentPath = ruleRes.path || []; const newRes = RuleRunReturn.Failing(value, ruleRes.message); newRes.path = [key, ...currentPath]; return newRes; } //#endregion //#region src/rules/schemaRules/tuple.ts /** * Validates that a value is a fixed-length array (tuple) where each position * matches the corresponding rule. Enforces exact length unless trailing * elements use enforce.optional(). * * Parsed values are propagated: if a rule transforms its input (e.g. toNumber), * the parsed tuple returned via `.parse()` carries the transformed values. * * @param value - The array to validate * @param rules - One RuleInstance per tuple position * @returns RuleRunReturn indicating success or failure, with `.type` holding * the parsed tuple on success * * @example * ```typescript * // Eager API * enforce(['hello', 42]).tuple(enforce.isString(), enforce.isNumber()); * * // Lazy API * const coordSchema = enforce.tuple(enforce.isNumber(), enforce.isNumber()); * coordSchema.test([40.7, -74.0]); // true * coordSchema.test([40.7]); // false — too few * coordSchema.test([40.7, -74, 0]);// false — too many * ``` */ function tuple(value, ...rules) { if (!Array.isArray(value)) return RuleRunReturn.Failing(value); if ((0, vest_utils.greaterThan)(countRequired(rules), value.length) || (0, vest_utils.longerThan)(value, rules.length)) return RuleRunReturn.Failing(value); return validateElements(value, rules); } /** * Counts the number of required (non-optional) leading positions by scanning * backwards from the end of the rules array. Stops at the first non-optional * rule, so only *trailing* optionals reduce the required count. */ function countRequired(rules) { let count = rules.length; for (let i = rules.length - 1; i >= 0; i--) if (isOptionalRule(rules[i])) count = i; else break; return count; } /** * Iterates over each rule position, validates the corresponding array element, * and collects parsed output values. Returns early on the first failing element * with an index-based error path. */ function validateElements(value, rules) { const parsedTuple = []; for (let i = 0; i < rules.length; i++) { if (isBeyondArrayEnd(value, i, rules[i])) continue; const res = runElementRule(value[i], rules[i], i); if (!res.pass) return elementFailure(value, res, i); parsedTuple.push(res.type ?? value[i]); } return RuleRunReturn.Passing(parsedTuple); } /** * Checks whether the given index is past the array's actual length * and the corresponding rule is optional, meaning it can be skipped. */ function isBeyondArrayEnd(value, index, rule) { return index >= value.length && isOptionalRule(rule); } /** * Runs a single element's rule within an enforce context that carries * the element value and its positional index as metadata. */ function runElementRule(item, rule, index) { return ctx.run({ value: item, set: true, meta: { index } }, () => rule.run(item)); } /** * Builds a failing RuleRunReturn with an error path that includes the * tuple index, prepended to any nested path from the inner rule failure. * For example, a shape failure at index 1 on key "id" yields path ["1", "id"]. */ function elementFailure(value, res, index) { const failure = RuleRunReturn.Failing(value, res.message); failure.path = [index.toString(), ...res.path || []]; return failure; } /** * Determines whether a rule is optional by testing if it passes with undefined. * This mirrors how shape/loose detect optional fields — a rule wrapping * enforce.optional() will pass for undefined, while required rules will not. */ function isOptionalRule(rule) { if (!rule || !(0, vest_utils.isFunction)(rule.test)) return false; return rule.test(void 0); } //#endregion //#regio