import * as _apoyo_std from '@apoyo/std';
import { Dict, Tree, Result, Option, Refinement, Predicate, InverseRefinement, Enum } from '@apoyo/std';

declare const _uuid: unique symbol;
declare const _email: unique symbol;
declare const _date: unique symbol;
declare const _datetime: unique symbol;
declare const _int: unique symbol;
interface UUIDBrand {
    readonly [_uuid]: unknown;
}
interface EmailBrand {
    readonly [_email]: unknown;
}
interface DateBrand {
    readonly [_date]: unknown;
}
interface DatetimeBrand {
    readonly [_datetime]: unknown;
}
interface IntBrand {
    readonly [_int]: unknown;
}
/**
 * An UUID
 */
declare type UUID = string & UUIDBrand;
/**
 * An email
 */
declare type Email = string & EmailBrand;
/**
 * A number without a decimal point
 */
declare type Int = number & IntBrand;
declare namespace ISO {
    /**
     * A date string with the following format:
     *
     * YYYY-MM-DD
     */
    type Date = string & DateBrand;
    /**
     * An ISO datetime string with the following format:
     *
     * YYYY-MM-DD HH:mm:ssZ
     */
    type Datetime = string & DatetimeBrand;
}

declare const enum ErrorCode {
    REQUIRED = "required",
    STRING = "string",
    STRING_LENGTH = "string.length",
    STRING_MIN = "string.min",
    STRING_MAX = "string.max",
    STRING_PATTERN = "string.pattern",
    STRING_EMAIL = "string.email",
    STRING_UUID = "string.uuid",
    STRING_EQUALS = "string.equals",
    STRING_ONE_OF = "string.oneOf",
    STRING_DATE = "string.date",
    STRING_DATETIME = "string.datetime",
    DATE = "date",
    DATE_STRICT = "date.strict",
    DATE_MIN = "date.min",
    DATE_MAX = "date.max",
    NUMBER = "number",
    NUMBER_STRICT = "number.strict",
    NUMBER_FROM_STRING = "number.fromString",
    NUMBER_MIN = "number.min",
    NUMBER_MAX = "number.max",
    INT = "int",
    INT_STRICT = "int.strict",
    INT_FROM_STRING = "int.fromString",
    INT_MIN = "int.min",
    INT_MAX = "int.max",
    BOOL = "bool",
    BOOL_STRICT = "bool.strict",
    BOOL_FROM_STRING = "bool.fromString",
    BOOL_FROM_NUMBER = "bool.fromNumber",
    BOOL_EQUALS = "bool.equals",
    ARRAY = "array",
    ARRAY_NON_EMPTY = "array.nonEmpty",
    ARRAY_LENGTH = "array.length",
    ARRAY_MIN = "array.min",
    ARRAY_MAX = "array.max",
    DICT = "dict",
    ENUM = "enum.native",
    ENUM_LITERAL = "enum.literal",
    ENUM_IS_IN = "enum.isIn"
}

declare const enum DecodeErrorTag {
    VALUE = "DE.Value",
    ARRAY = "DE.ArrayLike",
    INDEX = "DE.Index",
    OBJECT = "DE.ObjectLike",
    KEY = "DE.Key",
    UNION = "DE.UnionLike",
    MEMBER = "DE.Member"
}
declare namespace DecodeError {
    interface Value {
        tag: DecodeErrorTag.VALUE;
        value: unknown;
        message: string;
        meta: Dict;
    }
    interface Key {
        tag: DecodeErrorTag.KEY;
        key: string;
        error: DecodeError;
    }
    interface Index {
        tag: DecodeErrorTag.INDEX;
        index: number;
        error: DecodeError;
    }
    interface Member {
        tag: DecodeErrorTag.MEMBER;
        index: number;
        error: DecodeError;
    }
    interface ArrayLike {
        tag: DecodeErrorTag.ARRAY;
        kind: string;
        errors: DecodeError.Index[];
    }
    interface ObjectLike {
        tag: DecodeErrorTag.OBJECT;
        kind: string;
        name?: string;
        errors: DecodeError.Key[];
    }
    interface UnionLike {
        tag: DecodeErrorTag.UNION;
        kind: string;
        name?: string;
        errors: DecodeError.Member[];
    }
    type Path = {
        tag: DecodeErrorTag.ARRAY;
        kind: string;
    } | {
        tag: DecodeErrorTag.INDEX;
        index: number;
    } | {
        tag: DecodeErrorTag.OBJECT;
        kind: string;
        name?: string;
    } | {
        tag: DecodeErrorTag.KEY;
        key: string;
    } | {
        tag: DecodeErrorTag.UNION;
        kind: string;
        name?: string;
    } | {
        tag: DecodeErrorTag.MEMBER;
        index: number;
    };
    interface Flat {
        value: unknown;
        message: string;
        meta: Dict;
        path: Path[];
    }
    interface Formatted {
        value: unknown;
        message: string;
        description: string;
        meta: Dict;
        path: string;
    }
}
declare type DecodeError = DecodeError.Value | DecodeError.ObjectLike | DecodeError.ArrayLike | DecodeError.UnionLike;
declare const DecodeError: {
    value: (value: unknown, message: string, meta?: Dict) => DecodeError.Value;
    key: (key: string, error: DecodeError) => DecodeError.Key;
    index: (index: number, error: DecodeError) => DecodeError.Index;
    member: (index: number, error: DecodeError) => DecodeError.Member;
    array: (errors: DecodeError.Index[]) => DecodeError.ArrayLike;
    object: (errors: DecodeError.Key[], name?: string) => DecodeError.ObjectLike;
    union: (errors: DecodeError.Member[], name?: string) => DecodeError.UnionLike;
    fold: <T>(cases: {
        value(err: DecodeError.Value): T;
        array(err: DecodeError.ArrayLike): T;
        object(err: DecodeError.ObjectLike): T;
        union(err: DecodeError.UnionLike): T;
    }) => (err: DecodeError) => T;
    toTree: (e: DecodeError) => Tree<string>;
    /**
     * @description
     * Format your `DecodeError` into a easily displayable string.
     */
    draw: (e: DecodeError) => string;
    /**
     * @description
     * Flatten your `DecodeError` into an `DecodeError.Flat`.
     * This allows you to more easily format custom errors.
     *
     * It is however recommended to simply use `DecodeError.format` or `DecodeError.formatBy` instead.
     */
    flatten: (e: DecodeError) => DecodeError.Flat[];
    /**
     * @description
     * Get full error description / stack from a `DecodeError.Flat`.
     * Use `DecodeError.flatten` to transform your `DecodeError` into an `DecodeError.Flat`
     */
    getDescription: (err: DecodeError.Flat, separator?: string) => string;
    /**
     * @description
     * Get full property path from a `DecodeError.Flat`.
     * Use `DecodeError.flatten` to transform your `DecodeError` into an `DecodeError.Flat`
     */
    getPath: (err: DecodeError.Flat) => string;
    /**
     * @description
     * Get formatted error from a `DecodeError.Flat`.
     * Use `DecodeError.flatten` to transform your `DecodeError` into an `DecodeError.Flat`
     */
    getFormatted: (e: DecodeError.Flat) => DecodeError.Formatted;
    /**
     * @description
     * Create a custom error formatter, to fully customize your error output
     */
    formatBy: <T_1>(fn: (flat: DecodeError.Flat) => T_1) => (e: DecodeError) => T_1[];
    /**
     * @description
     * Default `DecodeError.formatBy` implementation, that returns `DecodeError.Formatted` errors
     */
    format: (e: DecodeError) => DecodeError.Formatted[];
    /**
     * @deprecated Use `DecodeError.getDescription` instead
     *
     * @see `DecodeError.getDescription`
     */
    formatError: (e: DecodeError.Flat) => string;
};

declare type DecoderResult<A> = Result<A, DecodeError>;
declare function filter<A, B extends A>(fn: Refinement<A, B>, message: string, meta?: Dict<unknown>): <I>(value: Decoder<I, A>) => Decoder<I, B>;
declare function filter<A>(fn: Predicate<A>, message: string, meta?: Dict<unknown>): <I>(value: Decoder<I, A>) => Decoder<I, A>;
declare function reject<A, B extends A>(fn: Refinement<A, B>, message: string, meta?: Dict<unknown>): <I>(value: Decoder<I, A>) => Decoder<I, InverseRefinement<A, B>>;
declare function reject<A>(fn: Predicate<A>, message: string, meta?: Dict<unknown>): <I>(value: Decoder<I, A>) => Decoder<I, A>;
declare function validate<O>(decoder: Decoder<unknown, O>): (input: unknown) => DecoderResult<O>;
declare function validate<I, O>(decoder: Decoder<I, O>): (input: I) => DecoderResult<O>;
declare function defaultValue(value: never[]): <I, O extends any[]>(decoder: Decoder<I, O | undefined>) => Decoder<I, O>;
declare function defaultValue<T>(value: T): <I, O>(decoder: Decoder<I, O | undefined>) => Decoder<I, O | T>;
declare function union<I, O1, O2>(a: Decoder<I, O1>, b: Decoder<I, O2>): Decoder<I, O1 | O2>;
declare function union<I, O1, O2, O3>(a: Decoder<I, O1>, b: Decoder<I, O2>, c: Decoder<I, O3>): Decoder<I, O1 | O2 | O3>;
declare function union<I, O1, O2, O3, O4>(a: Decoder<I, O1>, b: Decoder<I, O2>, c: Decoder<I, O3>, d: Decoder<I, O4>): Decoder<I, O1 | O2 | O3 | O4>;
declare type Decoder<I, O> = {
    decode(input: I): DecoderResult<O>;
};
declare namespace Decoder {
    type TypeOf<A> = A extends Decoder<unknown, infer B> ? Option.Struct<B> : never;
    type InputOf<A> = A extends Decoder<infer B, unknown> ? Option.Struct<B> : never;
}
/**
 * @namespace Decoder
 *
 * @description
 *
 * A `Decoder` is a function, that from an input I to create an output O, or produce an DecodeError.
 * As such, a common use-case for `Decoder`s are type and value validations.
 *
 * This namespace contains the core utilities to:
 * - Create `Decoder`s
 * - Use or combine them
 * - Extract / infer the resulting type informations
 *
 * @example
 * ```ts
 * export const TodoDto = ObjectDecoder.struct({
 *   id: IntegerDecoder.int,
 *   title: TextDecoder.varchar(1, 100),
 *   description: pipe(
 *     TextDecoder.varchar(0, 2000),
 *     TextDecoder.nullable
 *   ),
 *   done: BooleanDecoder.boolean
 * })
 *
 * export interface TodoDto extends Decoder.TypeOf<typeof TodoDto> {}
 * ```
 */
declare const Decoder: {
    /**
     * @description
     * Create a new decoder
     */
    create: <I, O>(fn: (input: I) => DecoderResult<O>) => Decoder<I, O>;
    /**
     * @description
     * Creates a new decoder from a type guard
     *
     * @example
     * ```ts
     * const stringDecoder = Decoder.fromGuard(
     *   (input: unknown): input is string => typeof input === 'string',
     *   'value is not a string'
     * )
     *
     * expect(pipe('Hello', Decoder.validate(decoder), Result.isOk)).toBe(true)
     * expect(pipe(42, Decoder.validate(decoder), Result.isKo)).toBe(true)
     * ```
     */
    fromGuard: <I_1, O_1 extends I_1>(fn: Refinement<I_1, O_1>, message: string, meta?: Dict<unknown>) => Decoder<I_1, O_1>;
    /**
     * @description
     * Map over the resulting value of an `Decoder`
     *
     * @example
     * ```ts
     * const decoder = pipe(
     *   TextDecoder.string,
     *   Decoder.map(str => str.trim())
     * )
     *
     * expect(pipe('  Hello  ', Decoder.validate(decoder), Result.get)).toBe('Hello')
     * ```
     */
    map: <A, B>(fn: (input: A) => B) => <I_2>(decoder: Decoder<I_2, A>) => Decoder<I_2, B>;
    /**
     * @description
     * Map over the resulting error of an `Decoder`
     *
     * @example
     * ```ts
     * const decoder = pipe(
     *   TextDecoder.string,
     *   Decoder.mapError(err => DecodeError.object([
     *     DecodeError.key('firstName', err)
     *   ]))
     * )
     * ```
     */
    mapError: <I_3>(fn: (err: DecodeError, input: I_3) => DecodeError) => <A_1>(decoder: Decoder<I_3, A_1>) => Decoder<I_3, A_1>;
    /**
     * @description
     * Catch the validation error and create a new error with the given message.
     *
     * @example
     * ```ts
     * const decoder = pipe(
     *   Decoder.union(
     *     NumberDecoder.number,
     *     NumberDecoder.fromString
     *   ),
     *   Decoder.withMessage('The given value is not a number', {
     *     code: 'invalid_number'
     *   })
     * )
     *
     * const expectedError = DecodeError.value('  Hello  ', 'This value is not a number', {
     *   code: 'invalid_number'
     * })
     * expect(pipe('  Hello  ', Decoder.validate(decoder))).toEqual(Result.ko(expectedError))
     * ```
     */
    withMessage: (msg: string, meta?: Dict<unknown>) => <I_4, A_2>(decoder: Decoder<I_4, A_2>) => Decoder<I_4, A_2>;
    /**
     * @description
     * Add a custom validation function to an `Decoder`.
     *
     * Compared to `Decoder.guard`, this function gives more control and allows the user to modify the resulting value.
     *
     * @see `Decoder.guard`
     * @see `Decoder.filter`
     * @see `Decoder.reject`
     *
     * @example
     * ```ts
     * const validateAge = (date: Date): Result<string, DecodeError> => {
     *   const now = new Date()
     *
     *   if (date.getFullYear() < now.getFullYear() - 100) {
     *     return Result.ko(DecodeError.value(dob, 'Date of birth is more than 100 years ago'))
     *   }
     *   if (date.getFullYear() > now.getFullYear() - 18) {
     *     return Result.ko(DecodeError.value(dob, 'Date of birth is less than 18 years ago'))
     *   }
     *   return Result.ok(dob)
     * }
     *
     * const birthdayDecoder = pipe(
     *   DateDecoder.date,
     *   Decoder.parse(validateAge)
     * )
     *
     * expect(pipe('1930-01-01', Decoder.validate(birthdayDecoder), Result.isOk)).toBe(true)
     * expect(pipe('1920-01-01', Decoder.validate(birthdayDecoder), Result.isKo)).toBe(true)
     * ```
     */
    parse: <B_1, C>(fn: (input: B_1) => DecoderResult<C>) => <A_3>(decoder: Decoder<A_3, B_1>) => Decoder<A_3, C>;
    /**
     * @description
     * Chain another decoder to execute with the current input.
     * This allows you to dynamically compute the decoder to use depending on a value.
     */
    chain: <B_2, C_1>(fn: (input: B_2) => Decoder<B_2, C_1>) => <A_4>(decoder: Decoder<A_4, B_2>) => Decoder<A_4, C_1>;
    /**
     * @description
     * Add a custom validation function, returning either:
     * - A `DecodeError` if the input is incorrect
     * - `undefined` if there is no error to report.
     *
     * This function gives more control about the returned error than `Decoder.filter` or `Decoder.reject`, but does not allow the value to be modified.
     *
     * @see `Decoder.parse`
     * @see `Decoder.filter`
     * @see `Decoder.reject`
     *
     * @example
     * ```ts
     * const validateAge = (date: Date): Option<DecodeError> => {
     *   const now = new Date()
     *
     *   if (date.getFullYear() < now.getFullYear() - 100) {
     *     return DecodeError.value(dob, 'Date of birth is more than 100 years ago')
     *   }
     *   if (date.getFullYear() > now.getFullYear() - 18) {
     *     return DecodeError.value(dob, 'Date of birth is less than 18 years ago')
     *   }
     *   return undefined
     * }
     *
     * const birthdayDecoder = pipe(
     *   DateDecoder.date,
     *   Decoder.guard(validateAge)
     * )
     *
     * expect(pipe('1930-01-01', Decoder.validate(birthdayDecoder), Result.isOk)).toBe(true)
     * expect(pipe('1920-01-01', Decoder.validate(birthdayDecoder), Result.isKo)).toBe(true)
     * ```
     */
    guard: <O_2>(fn: (input: O_2) => Option<DecodeError>) => <A_5>(decoder: Decoder<A_5, O_2>) => Decoder<A_5, O_2>;
    /**
     * @description
     * Add a `Predicate` filter function to a `Decoder`.
     * If the value matches the `Predicate`, the value is kept.
     * Else, an DecodeError with the given message is returned.
     *
     * @see `Decoder.parse`
     * @see `Decoder.guard`
     * @see `Decoder.reject`
     *
     * @see `DateDecoder.min`
     * @see `DateDecoder.max`
     *
     * @example
     * ```ts
     * const maxAge = (age: number) => (dob: Date) => {
     *   const now = new Date()
     *   return date.getFullYear() > now.getFullYear() - age
     * }
     * const minAge = (age: number) => (dob: Date) => {
     *   const now = new Date()
     *   return date.getFullYear() < now.getFullYear() - age
     * }
     *
     * const birthdayDecoder = pipe(
     *   DateDecoder.date,
     *   Decoder.filter(maxAge(100), `Date of birth is more than 100 years ago`),
     *   Decoder.filter(minAge(18), `Date of birth is less than 18 years ago`)
     * )
     *
     * expect(pipe('1930-01-01', Decoder.validate(birthdayDecoder), Result.isOk)).toBe(true)
     * expect(pipe('1920-01-01', Decoder.validate(birthdayDecoder), Result.isKo)).toBe(true)
     * ```
     */
    filter: typeof filter;
    /**
     * @description
     * Add a `Predicate` filter function to a `Decoder`.
     * If the value matches the `Predicate`, an DecodeError with the given message is returned.
     * Else, the value is kept.
     *
     * @see `Decoder.parse`
     * @see `Decoder.guard`
     * @see `Decoder.filter`
     *
     * @example
     * ```ts
     * const decoder = pipe(
     *   TextDecoder.string,
     *   Decoder.reject(str => str.length === 0, `string should not be empty`)
     * )
     *
     * expect(pipe('Hello', Decoder.validate(decoder), Result.isOk)).toBe(true)
     * expect(pipe('', Decoder.validate(decoder), Result.isKo)).toBe(true)
     * ```
     */
    reject: typeof reject;
    /**
     * @description
     * Makes the value nullable. If the input is undefined, the decoder will return null instead.
     *
     * **Note**: If you want to transform an empty string to `null`, use `TextDecoder.nullable` instead.
     *
     * @see `Decoder.optional`
     * @see `TextDecoder.nullable`
     *
     * @example
     * ```ts
     * const decoder = pipe(
     *   TextDecoder.string,
     *   Decoder.nullable
     * )
     *
     * expect(pipe('Hello', Decoder.validate(decoder), Result.get)).toBe('Hello')
     * expect(pipe('', Decoder.validate(decoder), Result.get)).toBe('')
     * expect(pipe(null, Decoder.validate(decoder), Result.get)).toBe(null)
     * expect(pipe(undefined, Decoder.validate(decoder), Result.get)).toBe(null)
     * ```
     */
    nullable: <I_5, O_3>(decoder: Decoder<I_5, O_3>) => Decoder<I_5, O_3 | null>;
    /**
     * @description
     * Makes the value optional (allows `undefined`).
     *
     * **Note**: If you want to transform an empty string to `undefined`, use `TextDecoder.optional` instead.
     *
     * @see `Decoder.nullable`
     * @see `TextDecoder.optional`
     *
     * @example
     * ```ts
     * const decoder = pipe(
     *   TextDecoder.string,
     *   Decoder.optional
     * )
     *
     * expect(pipe('Hello', Decoder.validate(decoder), Result.get)).toBe('Hello')
     * expect(pipe('', Decoder.validate(decoder), Result.get)).toBe('')
     * expect(pipe(undefined, Decoder.validate(decoder), Result.get)).toBe(undefined)
     * ```
     */
    optional: <I_6, O_4>(decoder: Decoder<I_6, O_4>) => Decoder<I_6, O_4 | undefined>;
    /**
     * @description
     * Explicitely return an "input is required" error when input is "null" or "undefined"
     *
     * @example
     * ```ts
     * const decoder = pipe(
     *   TextDecoder.string,
     *   Decoder.required
     * )
     * ```
     */
    required: <I_7, O_5>(decoder: Decoder<I_7, O_5>) => Decoder<I_7, O_5>;
    /**
     * @description
     * This function allows the creation of recursive type decoders.
     *
     * @example
     * ```ts
     * interface Tree<T> {
     *   value: T
     *   forest: Tree<T>[]
     * }
     *
     * // Recursive types require manual typing
     * const TreeDecoder = <O>(decoder: Decoder<unknown, O>): Decoder<unknown, Tree<O>> =>
     *   Decoder.lazy(() =>
     *     ObjectDecoder.struct({
     *       value: decoder,
     *       forest: ArrayDecoder.array(TreeDecoder(decoder))
     *     })
     *   )
     *
     * const input: unknown = {
     *   value: 'Hello',
     *   forest: [
     *     {
     *        value: 'World',
     *        forest: []
     *     }
     *   ]
     * }
     *
     * expect(pipe(input, TreeDecoder(TextDecoder.string), Result.isOk)).toBe(true)
     * ```
     */
    lazy: <I_8, O_6>(fn: () => Decoder<I_8, O_6>) => Decoder<I_8, O_6>;
    /**
     * @description
     * This operator makes the decoder optional and returns the given value when the input is undefined.
     *
     * @example
     * ```ts
     * const decoder = pipe(
     *   BooleanDecoder.boolean,
     *   Decoder.default(false)
     * )
     *
     * expect(pipe(true, Decoder.validate(decoder), Result.get)).toEqual(true)
     * expect(pipe(false, Decoder.validate(decoder), Result.get)).toEqual(false)
     * expect(pipe(undefined, Decoder.validate(decoder), Result.get)).toEqual(false)
     * ```
     */
    default: typeof defaultValue;
    /**
     * @description
     * Creates a union `Decoder` that tries, in the given order, if the input is valid.
     *
     * @example
     * ```ts
     * const decoder = Decoder.union(
     *   NumberDecoder.number,
     *   NumberDecoder.fromString
     * )
     *
     * expect(pipe(42, Decoder.validate(decoder), Result.isOk)).toBe(true)
     * expect(pipe("42", Decoder.validate(decoder), Result.isOk)).toBe(true)
     * expect(pipe("Hello", Decoder.validate(decoder), Result.isKo)).toBe(true)
     * ```
     */
    union: typeof union;
    /**
     * @description
     * Utility to bind a given generated type to a `Decoder`
     */
    ref: <A_6>(decoder: Decoder<unknown, A_6>) => Decoder<unknown, A_6>;
    /**
     * @description
     * Validate an input by the given `Decoder`
     *
     * @example
     * ```
     * const result = pipe(
     *   input,
     *   Decoder.validate(TextDecoder.string)
     * )
     *
     * if (Result.isKo(result)) {
     *   console.log(result.ko)
     *   return
     * }
     *
     * const value = result.ok
     * console.log(value)
     * ```
     */
    validate: typeof validate;
    /**
     * @description
     * `Decoder` for an unknown value
     */
    unknown: Decoder<unknown, unknown>;
    /**
     * @description
     * `Decoder` for any value
     */
    any: Decoder<unknown, any>;
};

declare function oneOf<T extends string>(arr: T[]): Decoder<unknown, T>;
declare function oneOf<T extends string>(arr: Set<T>): Decoder<unknown, T>;
declare type TextDecoder<I> = Decoder<I, string>;
/**
 * @namespace TextDecoder
 *
 * @description
 * This namespace contains string decoders and additional utilities for string validations.
 */
declare const TextDecoder: {
    /**
     * @description
     * Check if the input is a string
     */
    string: TextDecoder<unknown>;
    /**
     * @description
     * Check the length of the string
     */
    length: (len: number) => <I>(value: Decoder<I, string>) => Decoder<I, string>;
    /**
     * @description
     * Check the minimum length of the string
     *
     * @example
     * ```ts
     * const decoder = pipe(
     *   TextDecoder.string,
     *   TextDecoder.min(1)
     * )
     *
     * expect(pipe('1', Decoder.validate(decoder), Result.isOk)).toBe(true)
     * expect(pipe('', Decoder.validate(decoder), Result.isKo)).toBe(true)
     * ```
     */
    min: (minLength: number) => <I>(value: Decoder<I, string>) => Decoder<I, string>;
    /**
     * @description
     * Check the maximum length of the string
     *
     * @example
     * ```ts
     * const decoder = pipe(
     *   TextDecoder.string,
     *   TextDecoder.max(5)
     * )
     *
     * expect(pipe('12345', Decoder.validate(decoder), Result.isOk)).toBe(true)
     * expect(pipe('123456', Decoder.validate(decoder), Result.isKo)).toBe(true)
     * ```
     */
    max: (maxLength: number) => <I>(value: Decoder<I, string>) => Decoder<I, string>;
    /**
     * @description
     * Check both the minimum and maximum length of the string
     *
     * @example
     * ```ts
     * const decoder = pipe(
     *   TextDecoder.string,
     *   TextDecoder.between(1, 100)
     * )
     *
     * expect(pipe('', Decoder.validate(decoder), Result.isKo)).toBe(true)
     * ```
     */
    between: (minLength: number, maxLength: number) => <I>(value: Decoder<I, string>) => Decoder<I, string>;
    /**
     * @description
     * Trim the string
     *
     * @example
     * ```ts
     * const decoder = pipe(
     *   TextDecoder.string,
     *   TextDecoder.trim,
     *   TextDecoder.between(1, 100)
     * )
     *
     * expect(pipe('     ', Decoder.validate(decoder), Result.isKo)).toBe(true)
     * ```
     */
    trim: <I_1>(decoder: Decoder<I_1, string>) => Decoder<I_1, string>;
    /**
     * @description
     * Check if the string matches a given pattern / regexp
     *
     * @example
     * ```ts
     * const decoder = pipe(
     *   TextDecoder.string,
     *   TextDecoder.pattern(/^Hello [a-zA-Z]+$/)
     * )
     *
     * expect(pipe('Hello world', Decoder.validate(decoder), Result.isOk)).toBe(true)
     * ```
     */
    pattern: <T extends string = string>(regexp: RegExp, message?: string, meta?: Dict) => <I_2>(value: Decoder<I_2, string>) => Decoder<I_2, T>;
    /**
     * @description
     * Check if the string is a date in the ISO format (YYYY-MM-DD)
     *
     * @example
     * ```ts
     * expect(pipe("2020-06-13", Decoder.validate(TextDecoder.date), Result.isOk)).toBe(true)
     * expect(pipe("Hello", Decoder.validate(TextDecoder.date), Result.isKo)).toBe(true)
     * ```
     */
    date: Decoder<unknown, ISO.Date>;
    /**
     * @description
     * Check if the string is a datetime in the ISO format (YYYY-MM-DD HH:mm:ss)
     *
     * @example
     * ```ts
     * expect(pipe("2020-06-13", Decoder.validate(TextDecoder.datetime), Result.isOk)).toBe(true)
     * expect(pipe("2020-06-13 13:53:23Z", Decoder.validate(TextDecoder.datetime), Result.isOk)).toBe(true)
     * ```
     */
    datetime: Decoder<unknown, ISO.Datetime>;
    /**
     * @description
     * Check if the input is an email
     *
     * @example
     * ```ts
     * expect(pipe("test@example.com", Decoder.validate(TextDecoder.email), Result.isOk)).toBe(true)
     * expect(pipe("test", Decoder.validate(TextDecoder.email), Result.isKo)).toBe(true)
     * ```
     */
    email: Decoder<unknown, Email>;
    /**
     * @description
     * Check if the input is an UUID
     */
    uuid: Decoder<unknown, UUID>;
    /**
     * @description
     * Check if the input is a string between the given length.
     *
     * @example
     * ```ts
     * // The following:
     * const decoder = TextDecoder.varchar(1, 100)
     * // is the same as:
     * const decoder = pipe(TextDecoder.string, TextDecoder.between(1, 100))
     * ```
     */
    varchar: (minLength: number, maxLength: number) => Decoder<unknown, string>;
    /**
     * @description
     * This makes the string nullable. If the string is empty, `null` is returned.
     *
     * @see `TextDecoder.optional`
     * @see `Decoder.nullable`
     *
     * @example
     * ```ts
     * const decoder = pipe(TextDecoder.string, TextDecoder.nullable)
     *
     * expect(pipe("Hello", Decoder.validate(decoder), Result.get)).toBe("Hello")
     * expect(pipe(null, Decoder.validate(decoder), Result.get)).toBe(null)
     * expect(pipe("", Decoder.validate(decoder), Result.get)).toBe(null)
     * ```
     */
    nullable: <I_3>(decoder: Decoder<I_3, string>) => Decoder<I_3, string | null>;
    /**
     * @description
     * This makes the string optional (allows `undefined`). If the string is empty, `undefined` is returned.
     *
     * @see `TextDecoder.nullable`
     * @see `Decoder.optional`
     *
     * @example
     * ```ts
     * const decoder = pipe(TextDecoder.string, TextDecoder.optional)
     *
     * expect(pipe("Hello", Decoder.validate(decoder), Result.get)).toBe("Hello")
     * expect(pipe(undefined, Decoder.validate(decoder), Result.get)).toBe(undefined)
     * expect(pipe("", Decoder.validate(decoder), Result.get)).toBe(undefined)
     * ```
     */
    optional: <I_4>(decoder: Decoder<I_4, string>) => Decoder<I_4, string | undefined>;
    /**
     * @deprecated Use `EnumDecoder.isIn` instead.
     *
     * @description
     * Check if the string is included in the given values
     *
     * @example
     * ```ts
     * const decoder = TextDecoder.oneOf(['todo', 'in-progress', 'done', 'archived'] as const)
     *
     * expect(pipe("todo", Decoder.validate(decoder), Result.isOk)).toBe(true)
     * expect(pipe("unknown", Decoder.validate(decoder), Result.isKo)).toBe(true)
     * ```
     */
    oneOf: typeof oneOf;
    /**
     * @deprecated Use `EnumDecoder.literal` instead.
     *
     * @description
     * Check if the string is included in the given values
     *
     * @example
     * ```ts
     * const decoder = TextDecoder.equals('ongoing')
     *
     * expect(pipe("todo", Decoder.validate(decoder), Result.isOk)).toBe(true)
     * expect(pipe("unknown", Decoder.validate(decoder), Result.isKo)).toBe(true)
     * ```
     */
    equals: <T_1 extends string>(value: T_1) => Decoder<unknown, T_1>;
    /**
     * @description
     * Escapes the HTML in the string.
     *
     * @example
     * ```ts
     * const decoder = pipe(
     *   TextDecoder.string,
     *   TextDecoder.trim,
     *   TextDecoder.htmlEscape,
     *   TextDecoder.between(1, 2000)
     * )
     *
     * const escaped = pipe(
     *   "<script>window.alert("Hello")</script>",
     *   Decoder.validate(decoder),
     *   Result.get
     * )
     *
     * expect(escaped).toBe('&lt;script&gt;window.alert(&quot;Hello&quot;)&lt;/script&gt;')
     * ```
     */
    htmlEscape: <I_1>(decoder: Decoder<I_1, string>) => Decoder<I_1, string>;
};

declare type NumberDecoder<I> = Decoder<I, number>;
/**
 * @namespace NumberDecoder
 *
 * @description
 * This namespace contains number decoders and additional utilities for number validations.
 */
declare const NumberDecoder: {
    /**
     * @description
     * Check if the input is a number.
     * If the number is NaN, the number will fail validation.
     *
     * This function is strict and does not autocast the input into a number.
     */
    strict: NumberDecoder<unknown>;
    /**
     * @description
     * Check if the input is a number.
     * If the number is NaN, the number will fail validation.
     *
     * This function will autocast the input into a number if possible.
     */
    number: NumberDecoder<unknown>;
    /**
     * @description
     * Check if the number is equal or greater to the given minimum
     *
     * @example
     * ```ts
     * const decoder = pipe(
     *   NumberDecoder.number,
     *   NumberDecoder.min(1)
     * )
     *
     * expect(pipe(1, Decoder.validate(decoder), Result.isOk)).toBe(true)
     * expect(pipe(0, Decoder.validate(decoder), Result.isKo)).toBe(true)
     * ```
     */
    min: (minimum: number) => <I>(value: Decoder<I, number>) => Decoder<I, number>;
    /**
     * @description
     * Check if the number is equal or greater to the given minimum
     *
     * @example
     * ```ts
     * const decoder = pipe(
     *   NumberDecoder.number,
     *   NumberDecoder.max(100)
     * )
     *
     * expect(pipe(100, Decoder.validate(decoder), Result.isOk)).toBe(true)
     * expect(pipe(101, Decoder.validate(decoder), Result.isKo)).toBe(true)
     * ```
     */
    max: (maximum: number) => <I>(value: Decoder<I, number>) => Decoder<I, number>;
    /**
     * @description
     * Check if the number is between to the given minimum and maximum
     *
     * @example
     * ```ts
     * const decoder = pipe(
     *   NumberDecoder.number,
     *   NumberDecoder.between(1, 100)
     * )
     *
     * expect(pipe(1, Decoder.validate(decoder), Result.isOk)).toBe(true)
     * expect(pipe(100, Decoder.validate(decoder), Result.isOk)).toBe(true)
     * expect(pipe(0, Decoder.validate(decoder), Result.isKo)).toBe(true)
     * expect(pipe(101, Decoder.validate(decoder), Result.isKo)).toBe(true)
     * ```
     */
    between: (minimum: number, maximum: number) => <I>(value: Decoder<I, number>) => Decoder<I, number>;
    /**
     * @description
     * Check if the input is a number between the given minimum and maximum.
     *
     * @example
     * ```ts
     * // The following:
     * const decoder = NumberDecoder.range(1, 100)
     * // is the same as:
     * const decoder = pipe(NumberDecoder.number, NumberDecoder.between(1, 100))
     * ```
     */
    range: (minimum: number, maximum: number) => Decoder<unknown, number>;
    /**
     * @description
     * Decodes an input from a string into a number.
     *
     * @example
     * ```
     * expect(pipe('10.13', Decoder.validate(NumberDecoder.fromString), Result.get)).toBe(10.13)
     * expect(pipe('Hello', Decoder.validate(NumberDecoder.fromString), Result.isKo)).toBe(true)
     * ```
     */
    fromString: Decoder<unknown, number>;
};

declare type IntegerDecoder<I> = Decoder<I, Int>;
/**
 * @namespace IntegerDecoder
 *
 * @description
 * This namespace contains integer decoders and additional utilities for integer validations.
 */
declare const IntegerDecoder: {
    /**
     * @description
     * Check if the input is an integer.
     * If the integer is NaN or contains decimals, the number will fail validation.
     *
     * This function is strict and does not autocast the input into a number.
     */
    strict: IntegerDecoder<unknown>;
    /**
     * @description
     * Check if the input is an integer.
     * If the integer is NaN or contains decimals, the number will fail validation.
     *
     * This function will autocast the input into a number if possible.
     */
    int: IntegerDecoder<unknown>;
    /**
     * @description
     * Check if the number is equal or greater to the given minimum
     *
     * @example
     * ```ts
     * const decoder = pipe(
     *   IntegerDecoder.number,
     *   IntegerDecoder.min(1)
     * )
     *
     * expect(pipe(1, Decoder.validate(decoder), Result.isOk)).toBe(true)
     * expect(pipe(0, Decoder.validate(decoder), Result.isKo)).toBe(true)
     * ```
     */
    min: (minimum: number) => <I>(decoder: IntegerDecoder<I>) => IntegerDecoder<I>;
    /**
     * @description
     * Check if the number is equal or greater to the given minimum
     *
     * @example
     * ```ts
     * const decoder = pipe(
     *   IntegerDecoder.number,
     *   IntegerDecoder.max(100)
     * )
     *
     * expect(pipe(100, Decoder.validate(decoder), Result.isOk)).toBe(true)
     * expect(pipe(101, Decoder.validate(decoder), Result.isKo)).toBe(true)
     * ```
     */
    max: (maximum: number) => <I_1>(decoder: IntegerDecoder<I_1>) => IntegerDecoder<I_1>;
    /**
     * @description
     * Check if the number is between to the given minimum and maximum
     *
     * @example
     * ```ts
     * const decoder = pipe(
     *   IntegerDecoder.number,
     *   IntegerDecoder.between(1, 100)
     * )
     *
     * expect(pipe(1, Decoder.validate(decoder), Result.isOk)).toBe(true)
     * expect(pipe(100, Decoder.validate(decoder), Result.isOk)).toBe(true)
     * expect(pipe(0, Decoder.validate(decoder), Result.isKo)).toBe(true)
     * expect(pipe(101, Decoder.validate(decoder), Result.isKo)).toBe(true)
     * ```
     */
    between: (minimum: number, maximum: number) => <I_2>(decoder: IntegerDecoder<I_2>) => IntegerDecoder<I_2>;
    /**
     * @description
     * Check if the input is a number between the given minimum and maximum.
     *
     * @example
     * ```ts
     * // The following:
     * const decoder = IntegerDecoder.range(1, 100)
     * // is the same as:
     * const decoder = pipe(IntegerDecoder.number, IntegerDecoder.between(1, 100))
     * ```
     */
    range: (minimum: number, maximum: number) => IntegerDecoder<unknown>;
    /**
     * @description
     * Check if the input is a positive integer.
     * If the integer is NaN or contains decimals, the number will fail validation.
     */
    positive: IntegerDecoder<unknown>;
    /**
     * @description
     * Decodes an input from a string into an integer.
     *
     * @example
     * ```
     * expect(pipe('10', Decoder.validate(IntegerDecoder.fromString), Result.get)).toBe(10)
     * expect(pipe('Hello', Decoder.validate(IntegerDecoder.fromString), Result.isKo)).toBe(true)
     * ```
     */
    fromString: Decoder<unknown, Int>;
};

declare type BooleanDecoder<I> = Decoder<I, boolean>;
/**
 * @namespace BooleanDecoder
 *
 * @description
 * This namespace contains boolean decoders and additional utilities for boolean validations.
 */
declare const BooleanDecoder: {
    /**
     * @description
     * Check if the input is a boolean.
     *
     * This function is strict and does not autocast the input into a boolean.
     */
    strict: BooleanDecoder<unknown>;
    /**
     * @description
     * Check if the input is a boolean.
     *
     * This function will autocast the input into a boolean if possible.
     */
    boolean: BooleanDecoder<unknown>;
    /**
     * @description
     * Check if the boolean is true or false
     *
     * @example
     * ```
     * const decoder = BooleanDecoder.equals(true)
     *
     * expect(pipe(true, Decoder.validate(decoder), Result.isOk)).toBe(true)
     * expect(pipe(false, Decoder.validate(decoder), Result.isKo)).toBe(true)
     * ```
     */
    equals: <T extends boolean>(bool: T) => Decoder<unknown, T>;
    /**
     * @description
     * Decodes an input from a string into a boolean:
     * - 'true', 'yes', 'y', '1' becomes true
     * - 'false', 'no', 'n', '0' becomes false
     *
     * Any other value will fail validation.
     *
     * @example
     * ```
     * expect(pipe('yes', Decoder.validate(BooleanDecoder.fromString), Result.get)).toBe(true)
     * ```
     */
    fromString: Decoder<unknown, boolean>;
    /**
     * @description
     * Decodes an input from a number into a boolean:
     * - 1 becomes true
     * - 0 becomes false
     *
     * Any other value will fail validation.
     *
     * @example
     * ```
     * expect(pipe(1, Decoder.validate(BooleanDecoder.fromNumber), Result.get)).toBe(true)
     * ```
     */
    fromNumber: Decoder<unknown, boolean>;
};

declare type Struct<A extends Dict<unknown>> = {
    [P in keyof A]-?: Decoder<unknown, A[P]>;
};
declare function omit<I, O extends Dict, B extends keyof O>(props: B[]): (decoder: ObjectDecoder<I, O>) => ObjectDecoder<I, Omit<O, B>>;
declare function pick<I, O extends Dict, B extends keyof O>(props: B[]): (decoder: ObjectDecoder<I, O>) => ObjectDecoder<I, Pick<O, B>>;
declare function partial<I, O extends Dict>(decoder: ObjectDecoder<I, O>): ObjectDecoder<I, Partial<O>>;
declare function merge<I, O1 extends Dict>(a: ObjectDecoder<I, O1>): ObjectDecoder<I, O1>;
declare function merge<I, O1 extends Dict, O2 extends Dict>(a: ObjectDecoder<I, O1>, b: ObjectDecoder<I, O2>): ObjectDecoder<I, O2 & Omit<O1, keyof O2>>;
declare function merge<I, O1 extends Dict, O2 extends Dict, O3 extends Dict>(a: ObjectDecoder<I, O1>, b: ObjectDecoder<I, O2>, c: ObjectDecoder<I, O3>): ObjectDecoder<I, O3 & Omit<O2, keyof O3> & Omit<O1, keyof O3 | keyof O2>>;
declare function merge<I, O1 extends Dict, O2 extends Dict, O3 extends Dict, O4 extends Dict>(a: ObjectDecoder<I, O1>, b: ObjectDecoder<I, O2>, c: ObjectDecoder<I, O3>, d: ObjectDecoder<I, O4>): ObjectDecoder<I, O4 & Omit<O3, keyof O4> & Omit<O2, keyof O4 | keyof O3> & Omit<O1, keyof O4 | keyof O3 | keyof O2>>;
declare type SumTypes<K extends string, T extends Dict> = {
    [P in keyof T]: T[P] extends ObjectDecoder<any, infer A> ? {
        [KP in K]: P;
    } & A : never;
}[keyof T];
declare function sum<K extends string, I, T extends Dict<ObjectDecoder<I, any>>>(prop: K, cases: T): Decoder<I, SumTypes<K, T>>;
declare type ObjectDecoder<I, O extends Dict> = Decoder<I, O> & {
    props: Dict;
};
/**
 * @namespace ObjectDecoder
 *
 * @description
 * This namespace contains object decoders and additional utilities for object validations.
 */
declare const ObjectDecoder: {
    /**
     * @description
     * Check if the input is an object / record.
     * This function does not check the type of the properties.
     */
    unknownDict: Decoder<unknown, Dict<unknown>>;
    /**
     * @description
     * Check if the input is an record, where all properties are of the given type.
     */
    dict: <A>(decoder: Decoder<unknown, A>) => Decoder<unknown, Dict<A>>;
    /**
     * @description
     * Check if the input is an object, where all object properties match the given decoders.
     * All extraenous properties will be skipped and ignored.
     *
     * @example
     * ```ts
     * const TodoDto = ObjectDecoder.struct({
     *   id: IntegerDecoder.positive,
     *   title: TextDecoder.varchar(1, 100),
     *   done: BooleanDecoder.boolean
     * })
     *
     * const input: unknown = {
     *   id: 0,
     *   title: 'Wake up',
     *   description: 'Some description', // This property is not recognized by the decoder and will be ignored.
     *   done: false
     * }
     *
     * expect(pipe(input, Decoder.validate(TodoDto), Result.isOk)).toBe(true)
     * expect(pipe(input, Decoder.validate(TodoDto), Result.get)).toEqual({
     *   id: 0,
     *   title: 'Wake up',
     *   done: false
     * })
     * ```
     */
    struct: <A_1 extends Dict<any>>(props: Struct<A_1>, name?: string) => ObjectDecoder<unknown, A_1>;
    /**
     * @description
     * Omit given properties from an `ObjectDecoder`.
     * The resulting `ObjectDecoder` will not contain the omitted properties.
     *
     * @example
     * ```ts
     * const TodoPostDto = pipe(TodoDto, ObjectDecoder.omit(['id']))
     *
     * const input: unknown = {
     *   id: 0, // This property has been omitted and will be ignored.
     *   title: 'Wake up',
     *   done: false
     * }
     *
     * expect(pipe(input, Decoder.validate(TodoPostDto), Result.isOk)).toBe(true)
     * expect(pipe(input, Decoder.validate(TodoPostDto), Result.get)).toEqual({
     *   title: 'Wake up',
     *   done: false
     * })
     * ```
     */
    omit: typeof omit;
    /**
     * @description
     * Pick given properties from an `ObjectDecoder`.
     * The resulting `ObjectDecoder` will only contain the picked properties.
     *
     * @example
     * ```ts
     * const TodoPostDto = pipe(TodoDto, ObjectDecoder.pick(['title', 'done']))
     *
     * const input: unknown = {
     *   id: 0, // This property has not been picked and will be ignored.
     *   title: 'Wake up',
     *   done: false
     * }
     *
     * expect(pipe(input, Decoder.validate(TodoPostDto), Result.isOk)).toBe(true)
     * expect(pipe(input, Decoder.validate(TodoPostDto), Result.get)).toEqual({
     *   title: 'Wake up',
     *   done: false
     * })
     * ```
     */
    pick: typeof pick;
    /**
     * @description
     * Make all properties of an `ObjectDecoder` optional.
     */
    partial: typeof partial;
    /**
     * @description
     * Add a custom validation function to the `ObjectDecoder`.
     *
     * @example
     * ```ts
     * const SignupDto = pipe(
     *   ObjectDecoder.struct({
     *     email: TextDecoder.email,
     *     password: TextDecoder.varchar(5, 50),
     *     passwordRepeat: TextDecoder.varchar(5, 50)
     *   }),
     *   ObjectDecoder.guard(user => {
     *     return user.password === user.passwordRepeat
     *       ? undefined
     *       : DecodeError.object([
     *         DecodeError.key('passwordRepeat', DecodeError.value(user.passwordRepeat, `Password does not match password confirmation`))
     *       ])
     *   })
     * )
     *
     * const input: unknown = {
     *   email: 'test@example.com',
     *   password: '12345',
     *   passwordRepeat: '12345'
     * }
     *
     * expect(pipe(input, Decoder.validate(SignupDto), Result.isKo)).toBe(true)
     * ```
     */
    guard: <I, O extends Dict<any>>(fn: (input: O) => Option<DecodeError.Value | DecodeError.ObjectLike>) => (decoder: ObjectDecoder<I, O>) => ObjectDecoder<I, O>;
    /**
     * @description
     * Merge multiple `ObjectDecoder`s.
     * If a property has already been declared, the decoder for this property will be overwritten.
     *
     * @example
     * ```ts
     * const A = ObjectDecoder.struct({
     *   a: TextDecoder.string,
     *   ab: TextDecoder.string
     * })
     * const B = ObjectDecoder.struct({
     *   ab: NumberDecoder.number,
     *   b: NumberDecoder.number
     * })
     * const C = ObjectDecoder.merge(A, B)
     *
     * interface C extends Decoder.TypeOf<typeof C> {}
     *
     * // Interface C is now equals to:
     * type C = {
     *   a: string
     *   ab: number
     *   b: number
     * }
     *
     * // Note that "ab: TextDecoder.string" has been overwritten and will not be executed
     * ```
     */
    merge: typeof merge;
    /**
     * @description
     * By default, `ObjectDecoder.struct` will skip and ignore extra properties.
     * If you wish to keep extra properties (all properties not validated by the struct), you can use this util.
     *
     * @example
     * ```ts
     * const Response = pipe(
     *   ObjectDecoder.struct({
     *     status: EnumDecoder.literal('OK', 'KO'),
     *     message: TextDecoder.string,
     *   }),
     *   ObjectDecoder.additionalProperties
     * )
     * ```
     */
    additionalProperties: <I_1, O_1 extends Dict<any>>(decoder: ObjectDecoder<I_1, O_1>) => Decoder<I_1, O_1 & Dict<any>>;
    /**
     * @description
     * Execute a specific decoder depending on the value of a given "type" property.
     *
     * @example
     * ```ts
     * const Geom = ObjectDecoder.sum('type', {
     *   Circle: struct({
     *     radius: NumberDecoder.number
     *   }),
     *   Rectangle: struct({
     *     width: NumberDecoder.number,
     *     height: NumberDecoder.number
     *   })
     * })
     *
     * type Geom = Decoder.TypeOf<typeof Geom>
     * const geom: Geom = {
     *   type: 'Rectangle',
     *   height: 5
     * }
     * ```
     */
    sum: typeof sum;
};

declare type ArrayDecoder<I, O extends any[]> = Decoder<I, O>;
/**
 * @namespace ArrayDecoder
 *
 * @description
 * This namespace contains array decoders and additional utilities for array validations.
 */
declare const ArrayDecoder: {
    /**
     * @description
     * Check if the input is an array.
     */
    unknownArray: Decoder<unknown, unknown[]>;
    /**
     * @description
     * Check if the input is an array of a given type.
     */
    array: <A>(decoder: Decoder<unknown, A>) => ArrayDecoder<unknown, A[]>;
    /**
     * @description
     * Check if the input is a non empty array of a given type.
     */
    nonEmptyArray: <O>(decoder: Decoder<unknown, O>) => Decoder<unknown, _apoyo_std.NonEmptyArray<O>>;
    /**
     * @description
     * Check the length of the string
     */
    length: (len: number) => <D extends ArrayDecoder<any, any>>(value: D) => D;
    /**
     * @description
     * Check the minimum length of the array
     *
     * @example
     * ```ts
     * const decoder = pipe(
     *   ArrayDecoder.array(NumberDecoder.number),
     *   ArrayDecoder.min(1)
     * )
     *
     * expect(pipe([1], Decoder.validate(decoder), Result.isOk)).toBe(true)
     * expect(pipe([], Decoder.validate(decoder), Result.isKo)).toBe(true)
     * ```
     */
    min: (minLength: number) => <D_1 extends ArrayDecoder<any, any>>(value: D_1) => D_1;
    /**
     * @description
     * Check the maximum length of the array
     *
     * @example
     * ```ts
     * const decoder = pipe(
     *   ArrayDecoder.array(NumberDecoder.number),
     *   ArrayDecoder.max(5)
     * )
     *
     * expect(pipe([1,2,3,4,5], Decoder.validate(decoder), Result.isOk)).toBe(true)
     * expect(pipe([1,2,3,4,5,6], Decoder.validate(decoder), Result.isKo)).toBe(true)
     * ```
     */
    max: (maxLength: number) => <D_2 extends ArrayDecoder<any, any>>(value: D_2) => D_2;
    /**
     * @description
     * Check both the minimum and maximum length of the array
     *
     * @example
     * ```ts
     * const decoder = pipe(
     *   ArrayDecoder.array(NumberDecoder.number),
     *   ArrayDecoder.between(1, 5)
     * )
     *
     * expect(pipe([], Decoder.validate(decoder), Result.isKo)).toBe(true)
     * ```
     */
    between: (minLength: number, maxLength: number) => <D_1 extends ArrayDecoder<any, any>>(value: D_1) => D_1;
};

declare type DateDecoder<I, O extends Date> = Decoder<I, O>;
/**
 * @namespace DateDecoder
 *
 * @description
 * This namespace contains date decoders and additional utilities for date validations.
 */
declare const DateDecoder: {
    /**
     * @description
     * Check if the input is a date.
     *
     * @example
     * ```ts
     * expect(pipe('2021-01-01', Decoder.validate(DateDecoder.date), Result.isOk)).toBe(true)
     * expect(pipe('2021-01-01 12:00:00', Decoder.validate(DateDecoder.date), Result.isKo)).toBe(true)
     * ```
     */
    date: Decoder<unknown, Date>;
    /**
     * @description
     * Check if the input is a datetime.
     * @example
     * ```ts
     * expect(pipe('2021-01-01', Decoder.validate(DateDecoder.datetime), Result.isOk)).toBe(true)
     * expect(pipe('2021-01-01 12:00:00', Decoder.validate(DateDecoder.datetime), Result.isOk)).toBe(true)
     * expect(pipe('2021-01-01 12:00:00Z', Decoder.validate(DateDecoder.datetime), Result.isOk)).toBe(true)
     * ```
     */
    datetime: Decoder<unknown, Date>;
    /**
     * @description
     * Check if the input is a valid `Date` object.
     *
     * @example
     * ```ts
     * expect(pipe(new Date('2021-01-01'), Decoder.validate(DateDecoder.strict), Result.isOk)).toBe(true)
     * expect(pipe('2021-01-01', Decoder.validate(DateDecoder.strict), Result.isKo)).toBe(true)
     * ```
     */
    strict: Decoder<unknown, Date>;
    /**
     * @description
     * Check if the input can be cast to a valid `Date` object.
     *
     * expect(pipe(new Date('2021-01-01'), Decoder.validate(DateDecoder.native), Result.isOk)).toBe(true)
     * expect(pipe('2021-01-01', Decoder.validate(DateDecoder.native), Result.isOk)).toBe(true)
     * expect(pipe('2021-01-01 12:00:00Z', Decoder.validate(DateDecoder.native), Result.isOk)).toBe(true)
     */
    native: Decoder<unknown, Date>;
    /**
     * @description
     * Check if the date is above a specific date
     *
     * @example
     * ```ts
     * // The date needs to be above the current date
     * const futureDate = pipe(
     *   Decoder.date,
     *   Decoder.min(() => {
     *     const today = new Date().toISOString().split('T')[0]
     *     return new Date(today)
     *   })
     * )
     * ```
     */
    min: (minDate: Date | (() => Date)) => <A>(decoder: Decoder<A, Date>) => Decoder<A, Date>;
    /**
     * @description
     * Check if the date is above a specific date
     *
     * @example
     * ```ts
     * // The date cannot be above "now"
     * const futureDate = pipe(
     *   Decoder.datetime,
     *   Decoder.max(() => {
     *     const now = new Date()
     *     return now
     *   })
     * )
     * ```
     */
    max: (maxDate: Date | (() => Date)) => <A>(decoder: Decoder<A, Date>) => Decoder<A, Date>;
};

declare type Literal = string | number | boolean | null;
declare function isIn<T>(arr: T[] | Set<T>): Decoder<unknown, T>;
declare type EnumDecoder<I, O> = Decoder<I, O> & {
    values: Set<unknown>;
};
declare const EnumDecoder: {
    /**
     * @description
     * Checks if a value is included in the given enum
     *
     * @example
     * ```ts
     * enum Status {
     *   ACTIVE = "active",
     *   INACTIVE = "inactive",
     *   ARCHIVED = "archived"
     * }
     *
     * const decoder = EnumDecoder.native(Status)
     *
     * expect(pipe("active", Decoder.validate(decoder), Result.get)).toBe(Status.ACTIVE)
     * expect(pipe("xxx", Decoder.validate(decoder), Result.isKo)).toBe(true)
     * ```
     */
    native: <E extends Enum<E>>(enumType: E) => EnumDecoder<unknown, E[keyof E]>;
    /**
     * @deprecated Use `EnumDecoder.native` instead.
     *
     * @description
     * Checks if a value is included in the given enum
     *
     * @example
     * ```ts
     * enum Status {
     *   ACTIVE = "active",
     *   INACTIVE = "inactive",
     *   ARCHIVED = "archived"
     * }
     *
     * const decoder = EnumDecoder.from(Status)
     *
     * expect(pipe("active", Decoder.validate(decoder), Result.get)).toBe(Status.ACTIVE)
     * expect(pipe("xxx", Decoder.validate(decoder), Result.isKo)).toBe(true)
     * ```
     */
    from: <E extends Enum<E>>(enumType: E) => EnumDecoder<unknown, E[keyof E]>;
    /**
     * @description
     * Checks if a value is in the given list of constants
     *
     * @example
     * ```ts
     * const decoder = EnumDecoder.literal("active", "inactive", "archived")
     *
     * expect(pipe("active", Decoder.validate(decoder), Result.get)).toBe("active")
     * expect(pipe("xxx", Decoder.validate(decoder), Result.isKo)).toBe(true)
     * ```
     */
    literal: <A extends readonly [Literal, ...Literal[]]>(...values_0: A) => Decoder<unknown, A[number]>;
    /**
     * @description
     * Checks if a value is in the given list
     *
     * @example
     * ```ts
     * const decoder = EnumDecoder.isIn(["active", "inactive", "archived"])
     *
     * expect(pipe("active", Decoder.validate(decoder), Result.get)).toBe("active")
     * expect(pipe("xxx", Decoder.validate(decoder), Result.isKo)).toBe(true)
     * ```
     */
    isIn: typeof isIn;
};

export { ArrayDecoder, BooleanDecoder, DateDecoder, DecodeError, DecodeErrorTag, Decoder, DecoderResult, Email, EnumDecoder, ErrorCode, ISO, Int, IntegerDecoder, NumberDecoder, ObjectDecoder, TextDecoder, UUID };
