@sapphire/shapeshift
Advanced tools
Comparing version 1.1.0-next.6442f18.0 to 1.1.0-next.682bcae.0
/// <reference types="node" /> | ||
import { InspectOptionsStylized } from 'node:util'; | ||
declare type ArrayConstraintName = `s.array(T).length${'Lt' | 'Le' | 'Gt' | 'Ge' | 'Eq' | 'Ne'}`; | ||
declare type ArrayConstraintName = `s.array(T).length${'Lt' | 'Le' | 'Gt' | 'Ge' | 'Eq' | 'Ne' | 'Range' | 'RangeInclusive' | 'RangeExclusive'}`; | ||
declare function arrayLengthLt<T>(value: number): IConstraint<T[]>; | ||
@@ -11,2 +11,4 @@ declare function arrayLengthLe<T>(value: number): IConstraint<T[]>; | ||
declare function arrayLengthNe<T>(value: number): IConstraint<T[]>; | ||
declare function arrayLengthRange<T>(start: number, endBefore: number): IConstraint<T[]>; | ||
declare function arrayLengthRangeInclusive<T>(start: number, end: number): IConstraint<T[]>; | ||
@@ -77,14 +79,6 @@ declare type BigIntConstraintName = `s.bigint.${'lt' | 'le' | 'gt' | 'ge' | 'eq' | 'ne' | 'divisibleBy'}`; | ||
declare type ConstraintErrorNames = ArrayConstraintName | BigIntConstraintName | BooleanConstraintName | DateConstraintName | NumberConstraintName | StringConstraintName; | ||
declare class ConstraintError<T = unknown> extends BaseError { | ||
declare abstract class BaseConstraintError<T = unknown> extends BaseError { | ||
readonly constraint: ConstraintErrorNames; | ||
readonly given: T; | ||
readonly expected: string; | ||
constructor(constraint: ConstraintErrorNames, message: string, given: T, expected: string); | ||
toJSON(): { | ||
name: string; | ||
constraint: ConstraintErrorNames; | ||
given: T; | ||
expected: string; | ||
}; | ||
protected [customInspectSymbolStackLess](depth: number, options: InspectOptionsStylized): string; | ||
constructor(constraint: ConstraintErrorNames, message: string, given: T); | ||
} | ||
@@ -111,3 +105,3 @@ | ||
interface IConstraint<Input, Return extends Input = Input> { | ||
run(input: Input): Result<Return, ConstraintError<Input>>; | ||
run(input: Input): Result<Return, BaseConstraintError<Input>>; | ||
} | ||
@@ -128,2 +122,16 @@ | ||
declare class UnknownEnumValueError extends BaseError { | ||
readonly value: string | number; | ||
readonly enumKeys: string[]; | ||
readonly enumMappings: Map<string | number, string | number>; | ||
constructor(value: string | number, keys: string[], enumMappings: Map<string | number, string | number>); | ||
toJSON(): { | ||
name: string; | ||
value: string | number; | ||
enumKeys: string[]; | ||
enumMappings: [string | number, string | number][]; | ||
}; | ||
protected [customInspectSymbolStackLess](depth: number, options: InspectOptionsStylized): string; | ||
} | ||
declare class ValidationError extends BaseError { | ||
@@ -156,5 +164,6 @@ readonly validator: string; | ||
protected clone(): this; | ||
protected abstract handle(value: unknown): Result<T, ValidationError | CombinedError | CombinedPropertyError>; | ||
protected abstract handle(value: unknown): Result<T, ValidatorError>; | ||
protected addConstraint(constraint: IConstraint<T>): this; | ||
} | ||
declare type ValidatorError = ValidationError | CombinedError | CombinedPropertyError | UnknownEnumValueError; | ||
@@ -164,11 +173,20 @@ declare class ArrayValidator<T> extends BaseValidator<T[]> { | ||
constructor(validator: BaseValidator<T>, constraints?: readonly IConstraint<T[]>[]); | ||
lengthLt(length: number): this; | ||
lengthLe(length: number): this; | ||
lengthGt(length: number): this; | ||
lengthGe(length: number): this; | ||
lengthEq(length: number): this; | ||
lengthNe(length: number): this; | ||
lengthLt<N extends number>(length: N): BaseValidator<ExpandSmallerTuples<UnshiftTuple<[...Tuple<T, N>]>>>; | ||
lengthLe<N extends number>(length: N): BaseValidator<ExpandSmallerTuples<[...Tuple<T, N>]>>; | ||
lengthGt<N extends number>(length: N): BaseValidator<[...Tuple<T, N>, T, ...T[]]>; | ||
lengthGe<N extends number>(length: N): BaseValidator<[...Tuple<T, N>, ...T[]]>; | ||
lengthEq<N extends number>(length: N): BaseValidator<[...Tuple<T, N>]>; | ||
lengthNe(length: number): BaseValidator<[...T[]]>; | ||
lengthRange<S extends number, E extends number>(start: S, endBefore: E): BaseValidator<Exclude<ExpandSmallerTuples<UnshiftTuple<[...Tuple<T, E>]>>, ExpandSmallerTuples<UnshiftTuple<[...Tuple<T, S>]>>>>; | ||
lengthRangeInclusive<S extends number, E extends number>(startAt: S, endAt: E): BaseValidator<Exclude<ExpandSmallerTuples<[...Tuple<T, E>]>, ExpandSmallerTuples<UnshiftTuple<[...Tuple<T, S>]>>>>; | ||
lengthRangeExclusive<S extends number, E extends number>(startAfter: S, endBefore: E): BaseValidator<Exclude<ExpandSmallerTuples<UnshiftTuple<[...Tuple<T, E>]>>, ExpandSmallerTuples<[...Tuple<T, S>]>>>; | ||
protected clone(): this; | ||
protected handle(values: unknown): Result<T[], ValidationError | CombinedPropertyError>; | ||
} | ||
declare type UnshiftTuple<T extends [...any[]]> = T extends [T[0], ...infer Tail] ? Tail : never; | ||
declare type ExpandSmallerTuples<T extends [...any[]]> = T extends [T[0], ...infer Tail] ? T | ExpandSmallerTuples<Tail> : []; | ||
declare type Shift<A extends Array<any>> = ((...args: A) => void) extends (...args: [A[0], ...infer R]) => void ? R : never; | ||
declare type GrowExpRev<A extends Array<any>, N extends number, P extends Array<Array<any>>> = A['length'] extends N ? A : GrowExpRev<[...A, ...P[0]][N] extends undefined ? [...A, ...P[0]] : A, N, Shift<P>>; | ||
declare type GrowExp<A extends Array<any>, N extends number, P extends Array<Array<any>>> = [...A, ...A][N] extends undefined ? GrowExp<[...A, ...A], N, [A, ...P]> : GrowExpRev<A, N, P>; | ||
declare type Tuple<T, N extends number> = number extends N ? Array<T> : N extends 0 ? [] : N extends 1 ? [T] : GrowExp<[T], N, [[]]>; | ||
@@ -338,2 +356,9 @@ declare class BigIntValidator<T extends bigint> extends BaseValidator<T> { | ||
declare class TupleValidator<T extends any[]> extends BaseValidator<[...T]> { | ||
private readonly validators; | ||
constructor(validators: BaseValidator<[...T]>[], constraints?: readonly IConstraint<[...T]>[]); | ||
protected clone(): this; | ||
protected handle(values: unknown): Result<[...T], ValidationError | CombinedPropertyError>; | ||
} | ||
declare class UnionValidator<T> extends BaseValidator<T> { | ||
@@ -363,6 +388,20 @@ private validators; | ||
default(value: Exclude<T, undefined> | (() => Exclude<T, undefined>)): DefaultValidator<Exclude<T, undefined>>; | ||
protected handle(value: unknown): Result<T, ValidationError | CombinedError | CombinedPropertyError>; | ||
protected handle(value: unknown): Result<T, ValidatorError>; | ||
protected clone(): this; | ||
} | ||
declare class NativeEnumValidator<T extends NativeEnumLike> extends BaseValidator<T[keyof T]> { | ||
readonly enumShape: T; | ||
readonly hasNumericElements: boolean; | ||
private readonly enumKeys; | ||
private readonly enumMapping; | ||
constructor(enumShape: T); | ||
protected handle(value: unknown): Result<T[keyof T], ValidationError | UnknownEnumValueError>; | ||
protected clone(): this; | ||
} | ||
interface NativeEnumLike { | ||
[key: string]: string | number; | ||
[key: number]: string; | ||
} | ||
declare class Shapes { | ||
@@ -382,6 +421,8 @@ get string(): StringValidator<string>; | ||
enum<T>(...values: readonly T[]): UnionValidator<T>; | ||
nativeEnum<T extends NativeEnumLike>(enumShape: T): NativeEnumValidator<T>; | ||
literal<T>(value: T): BaseValidator<T>; | ||
instance<T>(expected: Constructor<T>): InstanceValidator<T>; | ||
union<T extends [...BaseValidator<any>[]]>(...validators: [...T]): UnionValidator<T[number] extends BaseValidator<infer U> ? U : never>; | ||
union<T extends [...BaseValidator<any>[]]>(...validators: [...T]): UnionValidator<Unwrap<T[number]>>; | ||
array<T>(validator: BaseValidator<T>): ArrayValidator<T>; | ||
tuple<T extends [...BaseValidator<any>[]]>(validators: [...T]): TupleValidator<UnwrapTuple<T>>; | ||
set<T>(validator: BaseValidator<T>): SetValidator<T>; | ||
@@ -391,3 +432,17 @@ record<T>(validator: BaseValidator<T>): RecordValidator<T>; | ||
} | ||
declare type UnwrapTuple<T extends [...any[]]> = T extends [infer Head, ...infer Tail] ? [Unwrap<Head>, ...UnwrapTuple<Tail>] : []; | ||
declare type Unwrap<T> = T extends BaseValidator<infer V> ? V : never; | ||
declare class ExpectedConstraintError<T = unknown> extends BaseConstraintError<T> { | ||
readonly expected: string; | ||
constructor(constraint: ConstraintErrorNames, message: string, given: T, expected: string); | ||
toJSON(): { | ||
name: string; | ||
constraint: ConstraintErrorNames; | ||
given: T; | ||
expected: string; | ||
}; | ||
protected [customInspectSymbolStackLess](depth: number, options: InspectOptionsStylized): string; | ||
} | ||
declare class MissingPropertyError extends BaseError { | ||
@@ -403,2 +458,14 @@ readonly property: PropertyKey; | ||
declare class MultiplePossibilitiesConstraintError<T = unknown> extends BaseConstraintError<T> { | ||
readonly expected: readonly string[]; | ||
constructor(constraint: ConstraintErrorNames, message: string, given: T, expected: readonly string[]); | ||
toJSON(): { | ||
name: string; | ||
constraint: ConstraintErrorNames; | ||
given: T; | ||
expected: readonly string[]; | ||
}; | ||
protected [customInspectSymbolStackLess](depth: number, options: InspectOptionsStylized): string; | ||
} | ||
declare class UnknownPropertyError extends BaseError { | ||
@@ -418,2 +485,2 @@ readonly property: PropertyKey; | ||
export { ArrayConstraintName, ArrayValidator, BaseError, BaseValidator, BigIntConstraintName, BigIntValidator, BooleanConstraintName, BooleanValidator, CombinedError, CombinedPropertyError, ConstraintError, ConstraintErrorNames, Constructor, DateConstraintName, DateValidator, DefaultValidator, ExpectedValidationError, IConstraint, InstanceValidator, LiteralValidator, MapValidator, MappedObjectValidator, MissingPropertyError, NeverValidator, NonNullObject, NullishValidator, NumberConstraintName, NumberValidator, ObjectValidator, ObjectValidatorStrategy, PassthroughValidator, RecordValidator, Result, SetValidator, Shapes, StringConstraintName, StringValidator, Type, UnionValidator, UnknownPropertyError, ValidationError, arrayLengthEq, arrayLengthGe, arrayLengthGt, arrayLengthLe, arrayLengthLt, arrayLengthNe, bigintDivisibleBy, bigintEq, bigintGe, bigintGt, bigintLe, bigintLt, bigintNe, booleanFalse, booleanTrue, dateEq, dateGe, dateGt, dateInvalid, dateLe, dateLt, dateNe, dateValid, numberDivisibleBy, numberEq, numberFinite, numberGe, numberGt, numberInt, numberLe, numberLt, numberNaN, numberNe, numberNeNaN, numberSafeInt, s, stringLengthEq, stringLengthGe, stringLengthGt, stringLengthLe, stringLengthLt, stringLengthNe }; | ||
export { ArrayConstraintName, ArrayValidator, BaseConstraintError, BaseError, BaseValidator, BigIntConstraintName, BigIntValidator, BooleanConstraintName, BooleanValidator, CombinedError, CombinedPropertyError, ConstraintErrorNames, Constructor, DateConstraintName, DateValidator, DefaultValidator, ExpectedConstraintError, ExpectedValidationError, IConstraint, InstanceValidator, LiteralValidator, MapValidator, MappedObjectValidator, MissingPropertyError, MultiplePossibilitiesConstraintError, NeverValidator, NonNullObject, NullishValidator, NumberConstraintName, NumberValidator, ObjectValidator, ObjectValidatorStrategy, PassthroughValidator, RecordValidator, Result, SetValidator, Shapes, StringConstraintName, StringValidator, TupleValidator, Type, UnionValidator, UnknownEnumValueError, UnknownPropertyError, Unwrap, ValidationError, ValidatorError, arrayLengthEq, arrayLengthGe, arrayLengthGt, arrayLengthLe, arrayLengthLt, arrayLengthNe, arrayLengthRange, arrayLengthRangeInclusive, bigintDivisibleBy, bigintEq, bigintGe, bigintGt, bigintLe, bigintLt, bigintNe, booleanFalse, booleanTrue, dateEq, dateGe, dateGt, dateInvalid, dateLe, dateLt, dateNe, dateValid, numberDivisibleBy, numberEq, numberFinite, numberGe, numberGt, numberInt, numberLe, numberLt, numberNaN, numberNe, numberNeNaN, numberSafeInt, s, stringLengthEq, stringLengthGe, stringLengthGt, stringLengthLe, stringLengthLt, stringLengthNe }; |
@@ -38,6 +38,8 @@ var SapphireShapeshift = (() => { | ||
CombinedPropertyError: () => CombinedPropertyError, | ||
ConstraintError: () => ConstraintError, | ||
ExpectedConstraintError: () => ExpectedConstraintError, | ||
ExpectedValidationError: () => ExpectedValidationError, | ||
MissingPropertyError: () => MissingPropertyError, | ||
MultiplePossibilitiesConstraintError: () => MultiplePossibilitiesConstraintError, | ||
Result: () => Result, | ||
UnknownEnumValueError: () => UnknownEnumValueError, | ||
UnknownPropertyError: () => UnknownPropertyError, | ||
@@ -133,3 +135,3 @@ ValidationError: () => ValidationError, | ||
// src/lib/errors/ConstraintError.ts | ||
// src/lib/errors/ExpectedConstraintError.ts | ||
var import_node_util = __require("util"); | ||
@@ -148,8 +150,16 @@ | ||
// src/lib/errors/ConstraintError.ts | ||
var ConstraintError = class extends BaseError { | ||
constructor(constraint, message, given, expected) { | ||
// src/lib/errors/BaseConstraintError.ts | ||
var BaseConstraintError = class extends BaseError { | ||
constructor(constraint, message, given) { | ||
super(message); | ||
this.constraint = constraint; | ||
this.given = given; | ||
} | ||
}; | ||
__name(BaseConstraintError, "BaseConstraintError"); | ||
// src/lib/errors/ExpectedConstraintError.ts | ||
var ExpectedConstraintError = class extends BaseConstraintError { | ||
constructor(constraint, message, given, expected) { | ||
super(constraint, message, given); | ||
this.expected = expected; | ||
@@ -168,3 +178,3 @@ } | ||
if (depth < 0) { | ||
return options.stylize(`[ConstraintError: ${constraint}]`, "special"); | ||
return options.stylize(`[ExpectedConstraintError: ${constraint}]`, "special"); | ||
} | ||
@@ -175,3 +185,3 @@ const newOptions = { ...options, depth: options.depth === null ? null : options.depth - 1 }; | ||
const given = (0, import_node_util.inspect)(this.given, newOptions).replaceAll("\n", padding); | ||
const header = `${options.stylize("ConstraintError", "special")} > ${constraint}`; | ||
const header = `${options.stylize("ExpectedConstraintError", "special")} > ${constraint}`; | ||
const message = options.stylize(this.message, "regexp"); | ||
@@ -188,3 +198,3 @@ const expectedBlock = ` | ||
}; | ||
__name(ConstraintError, "ConstraintError"); | ||
__name(ExpectedConstraintError, "ExpectedConstraintError"); | ||
@@ -221,3 +231,3 @@ // src/constraints/util/operators.ts | ||
run(input) { | ||
return comparator(input.length, length) ? Result.ok(input) : Result.err(new ConstraintError(name, "Invalid Array length", input, expected)); | ||
return comparator(input.length, length) ? Result.ok(input) : Result.err(new ExpectedConstraintError(name, "Invalid Array length", input, expected)); | ||
} | ||
@@ -257,2 +267,29 @@ }; | ||
__name(arrayLengthNe, "arrayLengthNe"); | ||
function arrayLengthRange(start, endBefore) { | ||
const expected = `expected.length >= ${start} && expected.length < ${endBefore}`; | ||
return { | ||
run(input) { | ||
return input.length >= start && input.length < endBefore ? Result.ok(input) : Result.err(new ExpectedConstraintError("s.array(T).lengthRange", "Invalid Array length", input, expected)); | ||
} | ||
}; | ||
} | ||
__name(arrayLengthRange, "arrayLengthRange"); | ||
function arrayLengthRangeInclusive(start, end) { | ||
const expected = `expected.length >= ${start} && expected.length <= ${end}`; | ||
return { | ||
run(input) { | ||
return input.length >= start && input.length <= end ? Result.ok(input) : Result.err(new ExpectedConstraintError("s.array(T).lengthRangeInclusive", "Invalid Array length", input, expected)); | ||
} | ||
}; | ||
} | ||
__name(arrayLengthRangeInclusive, "arrayLengthRangeInclusive"); | ||
function arrayLengthRangeExclusive(startAfter, endBefore) { | ||
const expected = `expected.length > ${startAfter} && expected.length < ${endBefore}`; | ||
return { | ||
run(input) { | ||
return input.length > startAfter && input.length < endBefore ? Result.ok(input) : Result.err(new ExpectedConstraintError("s.array(T).lengthRangeExclusive", "Invalid Array length", input, expected)); | ||
} | ||
}; | ||
} | ||
__name(arrayLengthRangeExclusive, "arrayLengthRangeExclusive"); | ||
@@ -353,2 +390,11 @@ // src/lib/errors/CombinedPropertyError.ts | ||
} | ||
lengthRange(start, endBefore) { | ||
return this.addConstraint(arrayLengthRange(start, endBefore)); | ||
} | ||
lengthRangeInclusive(startAt, endAt) { | ||
return this.addConstraint(arrayLengthRangeInclusive(startAt, endAt)); | ||
} | ||
lengthRangeExclusive(startAfter, endBefore) { | ||
return this.addConstraint(arrayLengthRangeExclusive(startAfter, endBefore)); | ||
} | ||
clone() { | ||
@@ -379,3 +425,3 @@ return Reflect.construct(this.constructor, [this.validator, this.constraints]); | ||
run(input) { | ||
return comparator(input, number) ? Result.ok(input) : Result.err(new ConstraintError(name, "Invalid bigint value", input, expected)); | ||
return comparator(input, number) ? Result.ok(input) : Result.err(new ExpectedConstraintError(name, "Invalid bigint value", input, expected)); | ||
} | ||
@@ -419,3 +465,3 @@ }; | ||
run(input) { | ||
return input % divider === 0n ? Result.ok(input) : Result.err(new ConstraintError("s.bigint.divisibleBy", "BigInt is not divisible", input, expected)); | ||
return input % divider === 0n ? Result.ok(input) : Result.err(new ExpectedConstraintError("s.bigint.divisibleBy", "BigInt is not divisible", input, expected)); | ||
} | ||
@@ -473,3 +519,3 @@ }; | ||
run(input) { | ||
return input ? Result.ok(input) : Result.err(new ConstraintError("s.boolean.true", "Invalid boolean value", input, "true")); | ||
return input ? Result.ok(input) : Result.err(new ExpectedConstraintError("s.boolean.true", "Invalid boolean value", input, "true")); | ||
} | ||
@@ -479,3 +525,3 @@ }; | ||
run(input) { | ||
return input ? Result.err(new ConstraintError("s.boolean.false", "Invalid boolean value", input, "false")) : Result.ok(input); | ||
return input ? Result.err(new ExpectedConstraintError("s.boolean.false", "Invalid boolean value", input, "false")) : Result.ok(input); | ||
} | ||
@@ -508,3 +554,3 @@ }; | ||
run(input) { | ||
return comparator(input.getTime(), number) ? Result.ok(input) : Result.err(new ConstraintError(name, "Invalid Date value", input, expected)); | ||
return comparator(input.getTime(), number) ? Result.ok(input) : Result.err(new ExpectedConstraintError(name, "Invalid Date value", input, expected)); | ||
} | ||
@@ -546,3 +592,3 @@ }; | ||
run(input) { | ||
return Number.isNaN(input.getTime()) ? Result.ok(input) : Result.err(new ConstraintError("s.date.eq(NaN)", "Invalid Date value", input, "expected === NaN")); | ||
return Number.isNaN(input.getTime()) ? Result.ok(input) : Result.err(new ExpectedConstraintError("s.date.eq(NaN)", "Invalid Date value", input, "expected === NaN")); | ||
} | ||
@@ -552,3 +598,3 @@ }; | ||
run(input) { | ||
return Number.isNaN(input.getTime()) ? Result.err(new ConstraintError("s.date.ne(NaN)", "Invalid Date value", input, "expected !== NaN")) : Result.ok(input); | ||
return Number.isNaN(input.getTime()) ? Result.err(new ExpectedConstraintError("s.date.ne(NaN)", "Invalid Date value", input, "expected !== NaN")) : Result.ok(input); | ||
} | ||
@@ -674,3 +720,3 @@ }; | ||
run(input) { | ||
return comparator(input, number) ? Result.ok(input) : Result.err(new ConstraintError(name, "Invalid number value", input, expected)); | ||
return comparator(input, number) ? Result.ok(input) : Result.err(new ExpectedConstraintError(name, "Invalid number value", input, expected)); | ||
} | ||
@@ -712,3 +758,3 @@ }; | ||
run(input) { | ||
return Number.isInteger(input) ? Result.ok(input) : Result.err(new ConstraintError("s.number.int", "Given value is not an integer", input, "Number.isInteger(expected) to be true")); | ||
return Number.isInteger(input) ? Result.ok(input) : Result.err(new ExpectedConstraintError("s.number.int", "Given value is not an integer", input, "Number.isInteger(expected) to be true")); | ||
} | ||
@@ -718,3 +764,3 @@ }; | ||
run(input) { | ||
return Number.isSafeInteger(input) ? Result.ok(input) : Result.err(new ConstraintError("s.number.safeInt", "Given value is not a safe integer", input, "Number.isSafeInteger(expected) to be true")); | ||
return Number.isSafeInteger(input) ? Result.ok(input) : Result.err(new ExpectedConstraintError("s.number.safeInt", "Given value is not a safe integer", input, "Number.isSafeInteger(expected) to be true")); | ||
} | ||
@@ -724,3 +770,3 @@ }; | ||
run(input) { | ||
return Number.isFinite(input) ? Result.ok(input) : Result.err(new ConstraintError("s.number.finite", "Given value is not finite", input, "Number.isFinite(expected) to be true")); | ||
return Number.isFinite(input) ? Result.ok(input) : Result.err(new ExpectedConstraintError("s.number.finite", "Given value is not finite", input, "Number.isFinite(expected) to be true")); | ||
} | ||
@@ -730,3 +776,3 @@ }; | ||
run(input) { | ||
return Number.isNaN(input) ? Result.ok(input) : Result.err(new ConstraintError("s.number.eq(NaN)", "Invalid number value", input, "expected === NaN")); | ||
return Number.isNaN(input) ? Result.ok(input) : Result.err(new ExpectedConstraintError("s.number.eq(NaN)", "Invalid number value", input, "expected === NaN")); | ||
} | ||
@@ -736,3 +782,3 @@ }; | ||
run(input) { | ||
return Number.isNaN(input) ? Result.err(new ConstraintError("s.number.ne(NaN)", "Invalid number value", input, "expected !== NaN")) : Result.ok(input); | ||
return Number.isNaN(input) ? Result.err(new ExpectedConstraintError("s.number.ne(NaN)", "Invalid number value", input, "expected !== NaN")) : Result.ok(input); | ||
} | ||
@@ -744,3 +790,3 @@ }; | ||
run(input) { | ||
return input % divider === 0 ? Result.ok(input) : Result.err(new ConstraintError("s.number.divisibleBy", "Number is not divisible", input, expected)); | ||
return input % divider === 0 ? Result.ok(input) : Result.err(new ExpectedConstraintError("s.number.divisibleBy", "Number is not divisible", input, expected)); | ||
} | ||
@@ -1081,17 +1127,24 @@ }; | ||
return false; | ||
const emailParts = email.split("@"); | ||
if (emailParts.length !== 2) | ||
const atIndex = email.indexOf("@"); | ||
if (atIndex === -1) | ||
return false; | ||
const account = emailParts[0]; | ||
if (account.length > 64) | ||
if (atIndex > 64) | ||
return false; | ||
const domain = emailParts[1]; | ||
if (domain.length > 255) | ||
const domainIndex = atIndex + 1; | ||
if (email.includes("@", domainIndex)) | ||
return false; | ||
const domainParts = domain.split("."); | ||
if (domainParts.length < 2) | ||
if (email.length - domainIndex > 255) | ||
return false; | ||
if (domainParts.some((part) => part.length > 63)) | ||
let dotIndex = email.indexOf(".", domainIndex); | ||
if (dotIndex === -1) | ||
return false; | ||
return accountRegex.test(account) && validateEmailDomain(domain); | ||
let lastDotIndex = domainIndex; | ||
do { | ||
if (dotIndex - lastDotIndex > 63) | ||
return false; | ||
lastDotIndex = dotIndex + 1; | ||
} while ((dotIndex = email.indexOf(".", lastDotIndex)) !== -1); | ||
if (email.length - lastDotIndex > 63) | ||
return false; | ||
return accountRegex.test(email.slice(0, atIndex)) && validateEmailDomain(email.slice(domainIndex)); | ||
} | ||
@@ -1108,2 +1161,87 @@ __name(validateEmail, "validateEmail"); | ||
// src/lib/errors/MultiplePossibilitiesConstraintError.ts | ||
var import_node_util5 = __require("util"); | ||
var MultiplePossibilitiesConstraintError = class extends BaseConstraintError { | ||
constructor(constraint, message, given, expected) { | ||
super(constraint, message, given); | ||
this.expected = expected; | ||
} | ||
toJSON() { | ||
return { | ||
name: this.name, | ||
constraint: this.constraint, | ||
given: this.given, | ||
expected: this.expected | ||
}; | ||
} | ||
[customInspectSymbolStackLess](depth, options) { | ||
const constraint = options.stylize(this.constraint, "string"); | ||
if (depth < 0) { | ||
return options.stylize(`[MultiplePossibilitiesConstraintError: ${constraint}]`, "special"); | ||
} | ||
const newOptions = { ...options, depth: options.depth === null ? null : options.depth - 1 }; | ||
const verticalLine = options.stylize("|", "undefined"); | ||
const padding = ` | ||
${verticalLine} `; | ||
const given = (0, import_node_util5.inspect)(this.given, newOptions).replaceAll("\n", padding); | ||
const header = `${options.stylize("MultiplePossibilitiesConstraintError", "special")} > ${constraint}`; | ||
const message = options.stylize(this.message, "regexp"); | ||
const expectedPadding = ` | ||
${verticalLine} - `; | ||
const expectedBlock = ` | ||
${options.stylize("Expected any of the following:", "string")}${expectedPadding}${this.expected.map((possible) => options.stylize(possible, "boolean")).join(expectedPadding)}`; | ||
const givenBlock = ` | ||
${options.stylize("Received:", "regexp")}${padding}${given}`; | ||
return `${header} | ||
${message} | ||
${expectedBlock} | ||
${givenBlock}`; | ||
} | ||
}; | ||
__name(MultiplePossibilitiesConstraintError, "MultiplePossibilitiesConstraintError"); | ||
// src/constraints/util/common/combinedResultFn.ts | ||
function combinedErrorFn(...fns) { | ||
switch (fns.length) { | ||
case 0: | ||
return () => null; | ||
case 1: | ||
return fns[0]; | ||
case 2: { | ||
const [fn0, fn1] = fns; | ||
return (...params) => fn0(...params) || fn1(...params); | ||
} | ||
default: { | ||
return (...params) => { | ||
for (const fn of fns) { | ||
const result = fn(...params); | ||
if (result) | ||
return result; | ||
} | ||
return null; | ||
}; | ||
} | ||
} | ||
} | ||
__name(combinedErrorFn, "combinedErrorFn"); | ||
// src/constraints/util/urlValidators.ts | ||
function createUrlValidators(options) { | ||
const fns = []; | ||
if (options?.allowedProtocols?.length) | ||
fns.push(allowedProtocolsFn(options.allowedProtocols)); | ||
if (options?.allowedDomains?.length) | ||
fns.push(allowedDomainsFn(options.allowedDomains)); | ||
return combinedErrorFn(...fns); | ||
} | ||
__name(createUrlValidators, "createUrlValidators"); | ||
function allowedProtocolsFn(allowedProtocols) { | ||
return (input, url) => allowedProtocols.includes(url.protocol) ? null : new MultiplePossibilitiesConstraintError("s.string.url", "Invalid URL protocol", input, allowedProtocols); | ||
} | ||
__name(allowedProtocolsFn, "allowedProtocolsFn"); | ||
function allowedDomainsFn(allowedDomains) { | ||
return (input, url) => allowedDomains.includes(url.hostname) ? null : new MultiplePossibilitiesConstraintError("s.string.url", "Invalid URL domain", input, allowedDomains); | ||
} | ||
__name(allowedDomainsFn, "allowedDomainsFn"); | ||
// src/constraints/StringConstraints.ts | ||
@@ -1113,3 +1251,3 @@ function stringLengthComparator(comparator, name, expected, length) { | ||
run(input) { | ||
return comparator(input.length, length) ? Result.ok(input) : Result.err(new ConstraintError(name, "Invalid string length", input, expected)); | ||
return comparator(input.length, length) ? Result.ok(input) : Result.err(new ExpectedConstraintError(name, "Invalid string length", input, expected)); | ||
} | ||
@@ -1152,3 +1290,3 @@ }; | ||
run(input) { | ||
return validateEmail(input) ? Result.ok(input) : Result.err(new ConstraintError("s.string.email", "Invalid email address", input, "expected to be an email address")); | ||
return validateEmail(input) ? Result.ok(input) : Result.err(new ExpectedConstraintError("s.string.email", "Invalid email address", input, "expected to be an email address")); | ||
} | ||
@@ -1161,3 +1299,3 @@ }; | ||
run(input) { | ||
return regex.test(input) ? Result.ok(input) : Result.err(new ConstraintError(type, "Invalid string format", input, expected)); | ||
return regex.test(input) ? Result.ok(input) : Result.err(new ExpectedConstraintError(type, "Invalid string format", input, expected)); | ||
} | ||
@@ -1168,16 +1306,15 @@ }; | ||
function stringUrl(options) { | ||
const validatorFn = createUrlValidators(options); | ||
return { | ||
run(input) { | ||
let url; | ||
try { | ||
const url = new URL(input); | ||
if (options?.allowedProtocols && !options.allowedProtocols.includes(url.protocol)) { | ||
return Result.err(new ConstraintError("s.string.url", "Invalid URL protocol", input, `expected ${url.protocol} to be one of: ${options.allowedProtocols.join(", ")}`)); | ||
} | ||
if (options?.allowedDomains && !options.allowedDomains.includes(url.hostname)) { | ||
return Result.err(new ConstraintError("s.string.url", "Invalid URL domain", input, `expected ${url.hostname} to be one of: ${options.allowedDomains.join(", ")}`)); | ||
} | ||
return Result.ok(input); | ||
url = new URL(input); | ||
} catch { | ||
return Result.err(new ConstraintError("s.string.url", "Invalid URL", input, "expected to match an URL")); | ||
return Result.err(new ExpectedConstraintError("s.string.url", "Invalid URL", input, "expected to match an URL")); | ||
} | ||
const validatorFnResult = validatorFn(input, url); | ||
if (validatorFnResult === null) | ||
return Result.ok(input); | ||
return Result.err(validatorFnResult); | ||
} | ||
@@ -1189,5 +1326,9 @@ }; | ||
const ipVersion = version ? `v${version}` : ""; | ||
const validatorFn = version === 4 ? import_node_net.isIPv4 : version === 6 ? import_node_net.isIPv6 : import_node_net.isIP; | ||
const name = `s.string.ip${ipVersion}`; | ||
const message = `Invalid IP${ipVersion} address`; | ||
const expected = `expected to be an IP${ipVersion} address`; | ||
return { | ||
run(input) { | ||
return (version === 4 ? (0, import_node_net.isIPv4)(input) : version === 6 ? (0, import_node_net.isIPv6)(input) : (0, import_node_net.isIP)(input)) ? Result.ok(input) : Result.err(new ConstraintError(`s.string.ip${ipVersion}`, `Invalid ip${ipVersion} address`, input, `expected to be an ip${ipVersion} address`)); | ||
return validatorFn(input) ? Result.ok(input) : Result.err(new ExpectedConstraintError(name, message, input, expected)); | ||
} | ||
@@ -1256,2 +1397,33 @@ }; | ||
// src/validators/TupleValidator.ts | ||
var TupleValidator = class extends BaseValidator { | ||
constructor(validators, constraints = []) { | ||
super(constraints); | ||
this.validators = []; | ||
this.validators = validators; | ||
} | ||
clone() { | ||
return Reflect.construct(this.constructor, [this.validators, this.constraints]); | ||
} | ||
handle(values) { | ||
if (!Array.isArray(values)) { | ||
return Result.err(new ValidationError("s.tuple(T)", "Expected an array", values)); | ||
} | ||
if (values.length !== this.validators.length) { | ||
return Result.err(new ValidationError("s.tuple(T)", `Expected an array of length ${this.validators.length}`, values)); | ||
} | ||
const errors = []; | ||
const transformed = []; | ||
for (let i = 0; i < values.length; i++) { | ||
const result = this.validators[i].run(values[i]); | ||
if (result.isOk()) | ||
transformed.push(result.value); | ||
else | ||
errors.push([i, result.error]); | ||
} | ||
return errors.length === 0 ? Result.ok(transformed) : Result.err(new CombinedPropertyError(errors)); | ||
} | ||
}; | ||
__name(TupleValidator, "TupleValidator"); | ||
// src/validators/UnionValidator.ts | ||
@@ -1380,2 +1552,77 @@ var UnionValidator = class extends BaseValidator { | ||
// src/lib/errors/UnknownEnumValueError.ts | ||
var UnknownEnumValueError = class extends BaseError { | ||
constructor(value, keys, enumMappings) { | ||
super("Expected the value to be one of the following enum values:"); | ||
this.value = value; | ||
this.enumKeys = keys; | ||
this.enumMappings = enumMappings; | ||
} | ||
toJSON() { | ||
return { | ||
name: this.name, | ||
value: this.value, | ||
enumKeys: this.enumKeys, | ||
enumMappings: [...this.enumMappings.entries()] | ||
}; | ||
} | ||
[customInspectSymbolStackLess](depth, options) { | ||
const value = options.stylize(this.value.toString(), "string"); | ||
if (depth < 0) { | ||
return options.stylize(`[UnknownEnumValueError: ${value}]`, "special"); | ||
} | ||
const padding = ` | ||
${options.stylize("|", "undefined")} `; | ||
const pairs = this.enumKeys.map((key) => { | ||
const enumValue = this.enumMappings.get(key); | ||
return `${options.stylize(key, "string")} or ${options.stylize(enumValue.toString(), typeof enumValue === "number" ? "number" : "string")}`; | ||
}).join(padding); | ||
const header = `${options.stylize("UnknownEnumValueError", "special")} > ${value}`; | ||
const message = options.stylize(this.message, "regexp"); | ||
const pairsBlock = `${padding}${pairs}`; | ||
return `${header} | ||
${message} | ||
${pairsBlock}`; | ||
} | ||
}; | ||
__name(UnknownEnumValueError, "UnknownEnumValueError"); | ||
// src/validators/NativeEnumValidator.ts | ||
var NativeEnumValidator = class extends BaseValidator { | ||
constructor(enumShape) { | ||
super(); | ||
this.hasNumericElements = false; | ||
this.enumMapping = /* @__PURE__ */ new Map(); | ||
this.enumShape = enumShape; | ||
this.enumKeys = Object.keys(enumShape).filter((key) => { | ||
return typeof enumShape[enumShape[key]] !== "number"; | ||
}); | ||
for (const key of this.enumKeys) { | ||
const enumValue = enumShape[key]; | ||
this.enumMapping.set(key, enumValue); | ||
this.enumMapping.set(enumValue, enumValue); | ||
if (typeof enumValue === "number") { | ||
this.hasNumericElements = true; | ||
this.enumMapping.set(`${enumValue}`, enumValue); | ||
} | ||
} | ||
} | ||
handle(value) { | ||
const typeOfValue = typeof value; | ||
if (typeOfValue === "number" && !this.hasNumericElements) { | ||
return Result.err(new ValidationError("s.nativeEnum(T)", "Expected the value to be a string", value)); | ||
} | ||
if (typeOfValue !== "string" && typeOfValue !== "number") { | ||
return Result.err(new ValidationError("s.nativeEnum(T)", "Expected the value to be a string or number", value)); | ||
} | ||
const casted = value; | ||
const possibleEnumValue = this.enumMapping.get(casted); | ||
return typeof possibleEnumValue === "undefined" ? Result.err(new UnknownEnumValueError(casted, this.enumKeys, this.enumMapping)) : Result.ok(possibleEnumValue); | ||
} | ||
clone() { | ||
return Reflect.construct(this.constructor, [this.enumShape]); | ||
} | ||
}; | ||
__name(NativeEnumValidator, "NativeEnumValidator"); | ||
// src/lib/Shapes.ts | ||
@@ -1422,2 +1669,5 @@ var Shapes = class { | ||
} | ||
nativeEnum(enumShape) { | ||
return new NativeEnumValidator(enumShape); | ||
} | ||
literal(value) { | ||
@@ -1437,2 +1687,5 @@ if (value instanceof Date) | ||
} | ||
tuple(validators) { | ||
return new TupleValidator(validators); | ||
} | ||
set(validator) { | ||
@@ -1439,0 +1692,0 @@ return new SetValidator(validator); |
@@ -31,6 +31,8 @@ "use strict"; | ||
CombinedPropertyError: () => CombinedPropertyError, | ||
ConstraintError: () => ConstraintError, | ||
ExpectedConstraintError: () => ExpectedConstraintError, | ||
ExpectedValidationError: () => ExpectedValidationError, | ||
MissingPropertyError: () => MissingPropertyError, | ||
MultiplePossibilitiesConstraintError: () => MultiplePossibilitiesConstraintError, | ||
Result: () => Result, | ||
UnknownEnumValueError: () => UnknownEnumValueError, | ||
UnknownPropertyError: () => UnknownPropertyError, | ||
@@ -126,3 +128,3 @@ ValidationError: () => ValidationError, | ||
// src/lib/errors/ConstraintError.ts | ||
// src/lib/errors/ExpectedConstraintError.ts | ||
var import_node_util = require("util"); | ||
@@ -141,8 +143,16 @@ | ||
// src/lib/errors/ConstraintError.ts | ||
var ConstraintError = class extends BaseError { | ||
constructor(constraint, message, given, expected) { | ||
// src/lib/errors/BaseConstraintError.ts | ||
var BaseConstraintError = class extends BaseError { | ||
constructor(constraint, message, given) { | ||
super(message); | ||
this.constraint = constraint; | ||
this.given = given; | ||
} | ||
}; | ||
__name(BaseConstraintError, "BaseConstraintError"); | ||
// src/lib/errors/ExpectedConstraintError.ts | ||
var ExpectedConstraintError = class extends BaseConstraintError { | ||
constructor(constraint, message, given, expected) { | ||
super(constraint, message, given); | ||
this.expected = expected; | ||
@@ -161,3 +171,3 @@ } | ||
if (depth < 0) { | ||
return options.stylize(`[ConstraintError: ${constraint}]`, "special"); | ||
return options.stylize(`[ExpectedConstraintError: ${constraint}]`, "special"); | ||
} | ||
@@ -168,3 +178,3 @@ const newOptions = { ...options, depth: options.depth === null ? null : options.depth - 1 }; | ||
const given = (0, import_node_util.inspect)(this.given, newOptions).replaceAll("\n", padding); | ||
const header = `${options.stylize("ConstraintError", "special")} > ${constraint}`; | ||
const header = `${options.stylize("ExpectedConstraintError", "special")} > ${constraint}`; | ||
const message = options.stylize(this.message, "regexp"); | ||
@@ -181,3 +191,3 @@ const expectedBlock = ` | ||
}; | ||
__name(ConstraintError, "ConstraintError"); | ||
__name(ExpectedConstraintError, "ExpectedConstraintError"); | ||
@@ -214,3 +224,3 @@ // src/constraints/util/operators.ts | ||
run(input) { | ||
return comparator(input.length, length) ? Result.ok(input) : Result.err(new ConstraintError(name, "Invalid Array length", input, expected)); | ||
return comparator(input.length, length) ? Result.ok(input) : Result.err(new ExpectedConstraintError(name, "Invalid Array length", input, expected)); | ||
} | ||
@@ -250,2 +260,29 @@ }; | ||
__name(arrayLengthNe, "arrayLengthNe"); | ||
function arrayLengthRange(start, endBefore) { | ||
const expected = `expected.length >= ${start} && expected.length < ${endBefore}`; | ||
return { | ||
run(input) { | ||
return input.length >= start && input.length < endBefore ? Result.ok(input) : Result.err(new ExpectedConstraintError("s.array(T).lengthRange", "Invalid Array length", input, expected)); | ||
} | ||
}; | ||
} | ||
__name(arrayLengthRange, "arrayLengthRange"); | ||
function arrayLengthRangeInclusive(start, end) { | ||
const expected = `expected.length >= ${start} && expected.length <= ${end}`; | ||
return { | ||
run(input) { | ||
return input.length >= start && input.length <= end ? Result.ok(input) : Result.err(new ExpectedConstraintError("s.array(T).lengthRangeInclusive", "Invalid Array length", input, expected)); | ||
} | ||
}; | ||
} | ||
__name(arrayLengthRangeInclusive, "arrayLengthRangeInclusive"); | ||
function arrayLengthRangeExclusive(startAfter, endBefore) { | ||
const expected = `expected.length > ${startAfter} && expected.length < ${endBefore}`; | ||
return { | ||
run(input) { | ||
return input.length > startAfter && input.length < endBefore ? Result.ok(input) : Result.err(new ExpectedConstraintError("s.array(T).lengthRangeExclusive", "Invalid Array length", input, expected)); | ||
} | ||
}; | ||
} | ||
__name(arrayLengthRangeExclusive, "arrayLengthRangeExclusive"); | ||
@@ -346,2 +383,11 @@ // src/lib/errors/CombinedPropertyError.ts | ||
} | ||
lengthRange(start, endBefore) { | ||
return this.addConstraint(arrayLengthRange(start, endBefore)); | ||
} | ||
lengthRangeInclusive(startAt, endAt) { | ||
return this.addConstraint(arrayLengthRangeInclusive(startAt, endAt)); | ||
} | ||
lengthRangeExclusive(startAfter, endBefore) { | ||
return this.addConstraint(arrayLengthRangeExclusive(startAfter, endBefore)); | ||
} | ||
clone() { | ||
@@ -372,3 +418,3 @@ return Reflect.construct(this.constructor, [this.validator, this.constraints]); | ||
run(input) { | ||
return comparator(input, number) ? Result.ok(input) : Result.err(new ConstraintError(name, "Invalid bigint value", input, expected)); | ||
return comparator(input, number) ? Result.ok(input) : Result.err(new ExpectedConstraintError(name, "Invalid bigint value", input, expected)); | ||
} | ||
@@ -412,3 +458,3 @@ }; | ||
run(input) { | ||
return input % divider === 0n ? Result.ok(input) : Result.err(new ConstraintError("s.bigint.divisibleBy", "BigInt is not divisible", input, expected)); | ||
return input % divider === 0n ? Result.ok(input) : Result.err(new ExpectedConstraintError("s.bigint.divisibleBy", "BigInt is not divisible", input, expected)); | ||
} | ||
@@ -466,3 +512,3 @@ }; | ||
run(input) { | ||
return input ? Result.ok(input) : Result.err(new ConstraintError("s.boolean.true", "Invalid boolean value", input, "true")); | ||
return input ? Result.ok(input) : Result.err(new ExpectedConstraintError("s.boolean.true", "Invalid boolean value", input, "true")); | ||
} | ||
@@ -472,3 +518,3 @@ }; | ||
run(input) { | ||
return input ? Result.err(new ConstraintError("s.boolean.false", "Invalid boolean value", input, "false")) : Result.ok(input); | ||
return input ? Result.err(new ExpectedConstraintError("s.boolean.false", "Invalid boolean value", input, "false")) : Result.ok(input); | ||
} | ||
@@ -501,3 +547,3 @@ }; | ||
run(input) { | ||
return comparator(input.getTime(), number) ? Result.ok(input) : Result.err(new ConstraintError(name, "Invalid Date value", input, expected)); | ||
return comparator(input.getTime(), number) ? Result.ok(input) : Result.err(new ExpectedConstraintError(name, "Invalid Date value", input, expected)); | ||
} | ||
@@ -539,3 +585,3 @@ }; | ||
run(input) { | ||
return Number.isNaN(input.getTime()) ? Result.ok(input) : Result.err(new ConstraintError("s.date.eq(NaN)", "Invalid Date value", input, "expected === NaN")); | ||
return Number.isNaN(input.getTime()) ? Result.ok(input) : Result.err(new ExpectedConstraintError("s.date.eq(NaN)", "Invalid Date value", input, "expected === NaN")); | ||
} | ||
@@ -545,3 +591,3 @@ }; | ||
run(input) { | ||
return Number.isNaN(input.getTime()) ? Result.err(new ConstraintError("s.date.ne(NaN)", "Invalid Date value", input, "expected !== NaN")) : Result.ok(input); | ||
return Number.isNaN(input.getTime()) ? Result.err(new ExpectedConstraintError("s.date.ne(NaN)", "Invalid Date value", input, "expected !== NaN")) : Result.ok(input); | ||
} | ||
@@ -667,3 +713,3 @@ }; | ||
run(input) { | ||
return comparator(input, number) ? Result.ok(input) : Result.err(new ConstraintError(name, "Invalid number value", input, expected)); | ||
return comparator(input, number) ? Result.ok(input) : Result.err(new ExpectedConstraintError(name, "Invalid number value", input, expected)); | ||
} | ||
@@ -705,3 +751,3 @@ }; | ||
run(input) { | ||
return Number.isInteger(input) ? Result.ok(input) : Result.err(new ConstraintError("s.number.int", "Given value is not an integer", input, "Number.isInteger(expected) to be true")); | ||
return Number.isInteger(input) ? Result.ok(input) : Result.err(new ExpectedConstraintError("s.number.int", "Given value is not an integer", input, "Number.isInteger(expected) to be true")); | ||
} | ||
@@ -711,3 +757,3 @@ }; | ||
run(input) { | ||
return Number.isSafeInteger(input) ? Result.ok(input) : Result.err(new ConstraintError("s.number.safeInt", "Given value is not a safe integer", input, "Number.isSafeInteger(expected) to be true")); | ||
return Number.isSafeInteger(input) ? Result.ok(input) : Result.err(new ExpectedConstraintError("s.number.safeInt", "Given value is not a safe integer", input, "Number.isSafeInteger(expected) to be true")); | ||
} | ||
@@ -717,3 +763,3 @@ }; | ||
run(input) { | ||
return Number.isFinite(input) ? Result.ok(input) : Result.err(new ConstraintError("s.number.finite", "Given value is not finite", input, "Number.isFinite(expected) to be true")); | ||
return Number.isFinite(input) ? Result.ok(input) : Result.err(new ExpectedConstraintError("s.number.finite", "Given value is not finite", input, "Number.isFinite(expected) to be true")); | ||
} | ||
@@ -723,3 +769,3 @@ }; | ||
run(input) { | ||
return Number.isNaN(input) ? Result.ok(input) : Result.err(new ConstraintError("s.number.eq(NaN)", "Invalid number value", input, "expected === NaN")); | ||
return Number.isNaN(input) ? Result.ok(input) : Result.err(new ExpectedConstraintError("s.number.eq(NaN)", "Invalid number value", input, "expected === NaN")); | ||
} | ||
@@ -729,3 +775,3 @@ }; | ||
run(input) { | ||
return Number.isNaN(input) ? Result.err(new ConstraintError("s.number.ne(NaN)", "Invalid number value", input, "expected !== NaN")) : Result.ok(input); | ||
return Number.isNaN(input) ? Result.err(new ExpectedConstraintError("s.number.ne(NaN)", "Invalid number value", input, "expected !== NaN")) : Result.ok(input); | ||
} | ||
@@ -737,3 +783,3 @@ }; | ||
run(input) { | ||
return input % divider === 0 ? Result.ok(input) : Result.err(new ConstraintError("s.number.divisibleBy", "Number is not divisible", input, expected)); | ||
return input % divider === 0 ? Result.ok(input) : Result.err(new ExpectedConstraintError("s.number.divisibleBy", "Number is not divisible", input, expected)); | ||
} | ||
@@ -1074,17 +1120,24 @@ }; | ||
return false; | ||
const emailParts = email.split("@"); | ||
if (emailParts.length !== 2) | ||
const atIndex = email.indexOf("@"); | ||
if (atIndex === -1) | ||
return false; | ||
const account = emailParts[0]; | ||
if (account.length > 64) | ||
if (atIndex > 64) | ||
return false; | ||
const domain = emailParts[1]; | ||
if (domain.length > 255) | ||
const domainIndex = atIndex + 1; | ||
if (email.includes("@", domainIndex)) | ||
return false; | ||
const domainParts = domain.split("."); | ||
if (domainParts.length < 2) | ||
if (email.length - domainIndex > 255) | ||
return false; | ||
if (domainParts.some((part) => part.length > 63)) | ||
let dotIndex = email.indexOf(".", domainIndex); | ||
if (dotIndex === -1) | ||
return false; | ||
return accountRegex.test(account) && validateEmailDomain(domain); | ||
let lastDotIndex = domainIndex; | ||
do { | ||
if (dotIndex - lastDotIndex > 63) | ||
return false; | ||
lastDotIndex = dotIndex + 1; | ||
} while ((dotIndex = email.indexOf(".", lastDotIndex)) !== -1); | ||
if (email.length - lastDotIndex > 63) | ||
return false; | ||
return accountRegex.test(email.slice(0, atIndex)) && validateEmailDomain(email.slice(domainIndex)); | ||
} | ||
@@ -1101,2 +1154,87 @@ __name(validateEmail, "validateEmail"); | ||
// src/lib/errors/MultiplePossibilitiesConstraintError.ts | ||
var import_node_util5 = require("util"); | ||
var MultiplePossibilitiesConstraintError = class extends BaseConstraintError { | ||
constructor(constraint, message, given, expected) { | ||
super(constraint, message, given); | ||
this.expected = expected; | ||
} | ||
toJSON() { | ||
return { | ||
name: this.name, | ||
constraint: this.constraint, | ||
given: this.given, | ||
expected: this.expected | ||
}; | ||
} | ||
[customInspectSymbolStackLess](depth, options) { | ||
const constraint = options.stylize(this.constraint, "string"); | ||
if (depth < 0) { | ||
return options.stylize(`[MultiplePossibilitiesConstraintError: ${constraint}]`, "special"); | ||
} | ||
const newOptions = { ...options, depth: options.depth === null ? null : options.depth - 1 }; | ||
const verticalLine = options.stylize("|", "undefined"); | ||
const padding = ` | ||
${verticalLine} `; | ||
const given = (0, import_node_util5.inspect)(this.given, newOptions).replaceAll("\n", padding); | ||
const header = `${options.stylize("MultiplePossibilitiesConstraintError", "special")} > ${constraint}`; | ||
const message = options.stylize(this.message, "regexp"); | ||
const expectedPadding = ` | ||
${verticalLine} - `; | ||
const expectedBlock = ` | ||
${options.stylize("Expected any of the following:", "string")}${expectedPadding}${this.expected.map((possible) => options.stylize(possible, "boolean")).join(expectedPadding)}`; | ||
const givenBlock = ` | ||
${options.stylize("Received:", "regexp")}${padding}${given}`; | ||
return `${header} | ||
${message} | ||
${expectedBlock} | ||
${givenBlock}`; | ||
} | ||
}; | ||
__name(MultiplePossibilitiesConstraintError, "MultiplePossibilitiesConstraintError"); | ||
// src/constraints/util/common/combinedResultFn.ts | ||
function combinedErrorFn(...fns) { | ||
switch (fns.length) { | ||
case 0: | ||
return () => null; | ||
case 1: | ||
return fns[0]; | ||
case 2: { | ||
const [fn0, fn1] = fns; | ||
return (...params) => fn0(...params) || fn1(...params); | ||
} | ||
default: { | ||
return (...params) => { | ||
for (const fn of fns) { | ||
const result = fn(...params); | ||
if (result) | ||
return result; | ||
} | ||
return null; | ||
}; | ||
} | ||
} | ||
} | ||
__name(combinedErrorFn, "combinedErrorFn"); | ||
// src/constraints/util/urlValidators.ts | ||
function createUrlValidators(options) { | ||
const fns = []; | ||
if (options?.allowedProtocols?.length) | ||
fns.push(allowedProtocolsFn(options.allowedProtocols)); | ||
if (options?.allowedDomains?.length) | ||
fns.push(allowedDomainsFn(options.allowedDomains)); | ||
return combinedErrorFn(...fns); | ||
} | ||
__name(createUrlValidators, "createUrlValidators"); | ||
function allowedProtocolsFn(allowedProtocols) { | ||
return (input, url) => allowedProtocols.includes(url.protocol) ? null : new MultiplePossibilitiesConstraintError("s.string.url", "Invalid URL protocol", input, allowedProtocols); | ||
} | ||
__name(allowedProtocolsFn, "allowedProtocolsFn"); | ||
function allowedDomainsFn(allowedDomains) { | ||
return (input, url) => allowedDomains.includes(url.hostname) ? null : new MultiplePossibilitiesConstraintError("s.string.url", "Invalid URL domain", input, allowedDomains); | ||
} | ||
__name(allowedDomainsFn, "allowedDomainsFn"); | ||
// src/constraints/StringConstraints.ts | ||
@@ -1106,3 +1244,3 @@ function stringLengthComparator(comparator, name, expected, length) { | ||
run(input) { | ||
return comparator(input.length, length) ? Result.ok(input) : Result.err(new ConstraintError(name, "Invalid string length", input, expected)); | ||
return comparator(input.length, length) ? Result.ok(input) : Result.err(new ExpectedConstraintError(name, "Invalid string length", input, expected)); | ||
} | ||
@@ -1145,3 +1283,3 @@ }; | ||
run(input) { | ||
return validateEmail(input) ? Result.ok(input) : Result.err(new ConstraintError("s.string.email", "Invalid email address", input, "expected to be an email address")); | ||
return validateEmail(input) ? Result.ok(input) : Result.err(new ExpectedConstraintError("s.string.email", "Invalid email address", input, "expected to be an email address")); | ||
} | ||
@@ -1154,3 +1292,3 @@ }; | ||
run(input) { | ||
return regex.test(input) ? Result.ok(input) : Result.err(new ConstraintError(type, "Invalid string format", input, expected)); | ||
return regex.test(input) ? Result.ok(input) : Result.err(new ExpectedConstraintError(type, "Invalid string format", input, expected)); | ||
} | ||
@@ -1161,16 +1299,15 @@ }; | ||
function stringUrl(options) { | ||
const validatorFn = createUrlValidators(options); | ||
return { | ||
run(input) { | ||
let url; | ||
try { | ||
const url = new URL(input); | ||
if (options?.allowedProtocols && !options.allowedProtocols.includes(url.protocol)) { | ||
return Result.err(new ConstraintError("s.string.url", "Invalid URL protocol", input, `expected ${url.protocol} to be one of: ${options.allowedProtocols.join(", ")}`)); | ||
} | ||
if (options?.allowedDomains && !options.allowedDomains.includes(url.hostname)) { | ||
return Result.err(new ConstraintError("s.string.url", "Invalid URL domain", input, `expected ${url.hostname} to be one of: ${options.allowedDomains.join(", ")}`)); | ||
} | ||
return Result.ok(input); | ||
url = new URL(input); | ||
} catch { | ||
return Result.err(new ConstraintError("s.string.url", "Invalid URL", input, "expected to match an URL")); | ||
return Result.err(new ExpectedConstraintError("s.string.url", "Invalid URL", input, "expected to match an URL")); | ||
} | ||
const validatorFnResult = validatorFn(input, url); | ||
if (validatorFnResult === null) | ||
return Result.ok(input); | ||
return Result.err(validatorFnResult); | ||
} | ||
@@ -1182,5 +1319,9 @@ }; | ||
const ipVersion = version ? `v${version}` : ""; | ||
const validatorFn = version === 4 ? import_node_net.isIPv4 : version === 6 ? import_node_net.isIPv6 : import_node_net.isIP; | ||
const name = `s.string.ip${ipVersion}`; | ||
const message = `Invalid IP${ipVersion} address`; | ||
const expected = `expected to be an IP${ipVersion} address`; | ||
return { | ||
run(input) { | ||
return (version === 4 ? (0, import_node_net.isIPv4)(input) : version === 6 ? (0, import_node_net.isIPv6)(input) : (0, import_node_net.isIP)(input)) ? Result.ok(input) : Result.err(new ConstraintError(`s.string.ip${ipVersion}`, `Invalid ip${ipVersion} address`, input, `expected to be an ip${ipVersion} address`)); | ||
return validatorFn(input) ? Result.ok(input) : Result.err(new ExpectedConstraintError(name, message, input, expected)); | ||
} | ||
@@ -1249,2 +1390,33 @@ }; | ||
// src/validators/TupleValidator.ts | ||
var TupleValidator = class extends BaseValidator { | ||
constructor(validators, constraints = []) { | ||
super(constraints); | ||
this.validators = []; | ||
this.validators = validators; | ||
} | ||
clone() { | ||
return Reflect.construct(this.constructor, [this.validators, this.constraints]); | ||
} | ||
handle(values) { | ||
if (!Array.isArray(values)) { | ||
return Result.err(new ValidationError("s.tuple(T)", "Expected an array", values)); | ||
} | ||
if (values.length !== this.validators.length) { | ||
return Result.err(new ValidationError("s.tuple(T)", `Expected an array of length ${this.validators.length}`, values)); | ||
} | ||
const errors = []; | ||
const transformed = []; | ||
for (let i = 0; i < values.length; i++) { | ||
const result = this.validators[i].run(values[i]); | ||
if (result.isOk()) | ||
transformed.push(result.value); | ||
else | ||
errors.push([i, result.error]); | ||
} | ||
return errors.length === 0 ? Result.ok(transformed) : Result.err(new CombinedPropertyError(errors)); | ||
} | ||
}; | ||
__name(TupleValidator, "TupleValidator"); | ||
// src/validators/UnionValidator.ts | ||
@@ -1373,2 +1545,77 @@ var UnionValidator = class extends BaseValidator { | ||
// src/lib/errors/UnknownEnumValueError.ts | ||
var UnknownEnumValueError = class extends BaseError { | ||
constructor(value, keys, enumMappings) { | ||
super("Expected the value to be one of the following enum values:"); | ||
this.value = value; | ||
this.enumKeys = keys; | ||
this.enumMappings = enumMappings; | ||
} | ||
toJSON() { | ||
return { | ||
name: this.name, | ||
value: this.value, | ||
enumKeys: this.enumKeys, | ||
enumMappings: [...this.enumMappings.entries()] | ||
}; | ||
} | ||
[customInspectSymbolStackLess](depth, options) { | ||
const value = options.stylize(this.value.toString(), "string"); | ||
if (depth < 0) { | ||
return options.stylize(`[UnknownEnumValueError: ${value}]`, "special"); | ||
} | ||
const padding = ` | ||
${options.stylize("|", "undefined")} `; | ||
const pairs = this.enumKeys.map((key) => { | ||
const enumValue = this.enumMappings.get(key); | ||
return `${options.stylize(key, "string")} or ${options.stylize(enumValue.toString(), typeof enumValue === "number" ? "number" : "string")}`; | ||
}).join(padding); | ||
const header = `${options.stylize("UnknownEnumValueError", "special")} > ${value}`; | ||
const message = options.stylize(this.message, "regexp"); | ||
const pairsBlock = `${padding}${pairs}`; | ||
return `${header} | ||
${message} | ||
${pairsBlock}`; | ||
} | ||
}; | ||
__name(UnknownEnumValueError, "UnknownEnumValueError"); | ||
// src/validators/NativeEnumValidator.ts | ||
var NativeEnumValidator = class extends BaseValidator { | ||
constructor(enumShape) { | ||
super(); | ||
this.hasNumericElements = false; | ||
this.enumMapping = /* @__PURE__ */ new Map(); | ||
this.enumShape = enumShape; | ||
this.enumKeys = Object.keys(enumShape).filter((key) => { | ||
return typeof enumShape[enumShape[key]] !== "number"; | ||
}); | ||
for (const key of this.enumKeys) { | ||
const enumValue = enumShape[key]; | ||
this.enumMapping.set(key, enumValue); | ||
this.enumMapping.set(enumValue, enumValue); | ||
if (typeof enumValue === "number") { | ||
this.hasNumericElements = true; | ||
this.enumMapping.set(`${enumValue}`, enumValue); | ||
} | ||
} | ||
} | ||
handle(value) { | ||
const typeOfValue = typeof value; | ||
if (typeOfValue === "number" && !this.hasNumericElements) { | ||
return Result.err(new ValidationError("s.nativeEnum(T)", "Expected the value to be a string", value)); | ||
} | ||
if (typeOfValue !== "string" && typeOfValue !== "number") { | ||
return Result.err(new ValidationError("s.nativeEnum(T)", "Expected the value to be a string or number", value)); | ||
} | ||
const casted = value; | ||
const possibleEnumValue = this.enumMapping.get(casted); | ||
return typeof possibleEnumValue === "undefined" ? Result.err(new UnknownEnumValueError(casted, this.enumKeys, this.enumMapping)) : Result.ok(possibleEnumValue); | ||
} | ||
clone() { | ||
return Reflect.construct(this.constructor, [this.enumShape]); | ||
} | ||
}; | ||
__name(NativeEnumValidator, "NativeEnumValidator"); | ||
// src/lib/Shapes.ts | ||
@@ -1415,2 +1662,5 @@ var Shapes = class { | ||
} | ||
nativeEnum(enumShape) { | ||
return new NativeEnumValidator(enumShape); | ||
} | ||
literal(value) { | ||
@@ -1430,2 +1680,5 @@ if (value instanceof Date) | ||
} | ||
tuple(validators) { | ||
return new TupleValidator(validators); | ||
} | ||
set(validator) { | ||
@@ -1450,6 +1703,8 @@ return new SetValidator(validator); | ||
CombinedPropertyError, | ||
ConstraintError, | ||
ExpectedConstraintError, | ||
ExpectedValidationError, | ||
MissingPropertyError, | ||
MultiplePossibilitiesConstraintError, | ||
Result, | ||
UnknownEnumValueError, | ||
UnknownPropertyError, | ||
@@ -1456,0 +1711,0 @@ ValidationError, |
{ | ||
"name": "@sapphire/shapeshift", | ||
"version": "1.1.0-next.6442f18.0", | ||
"version": "1.1.0-next.682bcae.0", | ||
"description": "Blazing fast input validation and transformation ⚡", | ||
@@ -14,3 +14,4 @@ "author": "@sapphire", | ||
"import": "./dist/index.mjs", | ||
"require": "./dist/index.js" | ||
"require": "./dist/index.js", | ||
"types": "./dist/index.d.ts" | ||
}, | ||
@@ -38,3 +39,3 @@ "sideEffects": false, | ||
"@favware/rollup-type-bundler": "^1.0.7", | ||
"@sapphire/eslint-config": "^4.2.1", | ||
"@sapphire/eslint-config": "^4.2.2", | ||
"@sapphire/prettier-config": "^1.3.0", | ||
@@ -44,7 +45,7 @@ "@sapphire/ts-config": "^3.3.1", | ||
"@types/node": "^17.0.8", | ||
"@typescript-eslint/eslint-plugin": "^5.12.1", | ||
"@typescript-eslint/parser": "^5.12.1", | ||
"@typescript-eslint/eslint-plugin": "^5.13.0", | ||
"@typescript-eslint/parser": "^5.13.0", | ||
"cz-conventional-changelog": "^3.3.0", | ||
"eslint": "^8.10.0", | ||
"eslint-config-prettier": "^8.4.0", | ||
"eslint-config-prettier": "^8.5.0", | ||
"eslint-plugin-prettier": "^4.0.0", | ||
@@ -54,3 +55,3 @@ "husky": "^7.0.4", | ||
"jest-circus": "^27.5.1", | ||
"lint-staged": "^12.3.4", | ||
"lint-staged": "^12.3.5", | ||
"prettier": "^2.5.1", | ||
@@ -60,7 +61,7 @@ "pretty-quick": "^3.1.3", | ||
"ts-jest": "^27.1.3", | ||
"ts-node": "^10.5.0", | ||
"ts-node": "^10.7.0", | ||
"tsup": "^5.11.13", | ||
"typedoc": "^0.22.12", | ||
"typedoc-plugin-mdn-links": "^1.0.5", | ||
"typescript": "^4.5.5" | ||
"typescript": "^4.6.2" | ||
}, | ||
@@ -67,0 +68,0 @@ "repository": { |
@@ -204,3 +204,3 @@ <div align="center"> | ||
ShapeShift includes a handful of string-specific validations: | ||
ShapeShift includes a handful of array-specific validations: | ||
@@ -214,7 +214,10 @@ ```typescript | ||
s.string.array.lengthNe(5); // Must not have exactly 5 elements | ||
s.string.array.lengthRange(0, 4); // Must have at least 0 elements and less than 4 elements (in math, that is [0, 4)) | ||
s.string.array.lengthRangeInclusive(0, 4); // Must have at least 0 elements and at most 4 elements (in math, that is [0, 4]) | ||
s.string.array.lengthRangeExclusive(0, 4); // Must have more than 0 element and less than 4 elements (in math, that is (0, 4)) | ||
``` | ||
> **Note**: `.lengthGt` and `.lengthGe` are overloaded and change the inferred type from 1 to 10. For example, `s.string.array.lengthGe(2)`'s inferred type is `[string, string, ...string[]]` // TODO | ||
> **Note**: All `.length` methods define tuple types with the given amount of elements. For example, `s.string.array.lengthGe(2)`'s inferred type is `[string, string, ...string[]]` | ||
#### Tuples // TODO | ||
#### Tuples | ||
@@ -221,0 +224,0 @@ Unlike arrays, tuples have a fixed number of elements and each element can have a different type: |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
573128
5275
538