n4s
Version:
typed schema validation version of enforce
1,737 lines (1,666 loc) • 76.9 kB
JavaScript
//#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