import type { DefinitionsOfSchemaType, ValidationFallbackCallbackReturnType } from './types';
import type { SchemaAdapter } from '../adapter';
import type { FieldAdapter } from '../adapter/fields';
import type { ValidationDataBasedOnType } from '../adapter/types';
import type { Validator } from '../validators/utils';
import type { StandardSchemaV1 } from '@standard-schema/spec';
export declare class Schema<TType extends {
    input: any;
    validate: any;
    internal: any;
    output: any;
    representation: any;
} = {
    input: any;
    validate: any;
    internal: any;
    output: any;
    representation: any;
}, TDefinitions extends DefinitionsOfSchemaType = DefinitionsOfSchemaType<SchemaAdapter & Palmares.PSchemaAdapter>> implements StandardSchemaV1<TType['input'], TType['output']> {
    readonly '~standard': StandardSchemaV1.Props<TType['input'], TType['output']>;
    protected $$type: string;
    protected fieldType: string;
    protected __beforeValidationCallbacks: Map<string, (adapterToUse: SchemaAdapter, fieldAdapter: FieldAdapter, schema: Schema<any, any> & {
        __validateByAdapter: Schema<any, any>['__validateByAdapter'];
    }, translatedSchemas: any[], value: TType['input'], path: ValidationFallbackCallbackReturnType['errors'][number]['path'], options: Parameters<Schema['__transformToAdapter']>[0]) => ReturnType<Schema['__validateByAdapter']>>;
    protected __cachedGetParent?: () => Schema<any, any>;
    protected set __getParent(value: () => Schema<any, any>);
    protected get __getParent(): (() => Schema<any, any>) | undefined;
    protected __alreadyAppliedModel?: Promise<any>;
    protected __runBeforeParseAndData?: (self: any) => Promise<void>;
    protected __rootFallbacksValidator: Validator;
    protected __saveCallback?: ((value: any) => (context: any) => Promise<TType['output']>) | ((value: any) => Promise<TType['output']>);
    protected __modelOmitCallback?: () => void;
    protected __parsers: Record<'high' | 'medium' | 'low', Map<string, (value: any) => {
        value: any;
        preventNextParsers: boolean;
    } | Promise<{
        value: any;
        preventNextParsers: boolean;
    }>>> & {
        _fallbacks: Set<string>;
    };
    protected __refinements: ((args: {
        value: any;
        context: any;
    }) => Promise<void | undefined | {
        code: string;
        message: string;
    }>)[];
    protected __nullable: {
        message: string;
        allow: boolean;
    };
    protected __optional: {
        message: string;
        allow: boolean;
    };
    protected __extends: {
        callback: (schema: any) => any;
        toStringCallback?: (schemaAsString: string) => string;
    } | undefined;
    protected __transformedSchemas: Record<string, {
        transformed: boolean;
        adapter: TDefinitions['schemaAdapter'];
        schemas: any[];
    }>;
    protected __defaultFunction: (() => Promise<TType['input'] | TType['output']>) | undefined;
    protected __toRepresentation: ((value: TType['output']) => TType['output']) | undefined;
    protected __toValidate: ((value: TType['input'], context: TDefinitions['context']) => TType['validate']) | undefined;
    protected __toInternal: ((value: TType['validate']) => TType['internal']) | undefined;
    protected __type: {
        message: string;
        check: (value: TType['input']) => boolean;
    };
    protected __getDefaultTransformedSchemas(): void;
    /**
     * This will validate the data with the fallbacks, so internally, without relaying on the schema adapter.
     * This is nice because we can support things that the schema adapter is not able to support by default.
     *
     * @param errorsAsHashedSet - The errors as a hashed set. This is used to prevent duplicate errors.
     * @param path - The path of the error.
     * @param parseResult - The result of the parse method.
     */
    private __validateByFallbacks;
    /**
     * This will validate by the adapter. In other words, we send the data to the schema adapter and then we validate
     * that data.
     * So understand that, first we send the data to the adapter, the adapter validates it, then, after we validate
     * from the adapter we validate with the fallbacks so we can do all of the extra validations not handled by
     * the adapter.
     *
     * @param value - The value to be validated.
     * @param errorsAsHashedSet - The errors as a hashed set. This is used to prevent duplicate errors on the validator.
     * @param path - The path of the error so we can construct an object with the nested paths of the error.
     * @param parseResult - The result of the parse method.
     *
     * @returns The result and the errors of the parse method.
     */
    protected __validateByAdapter(adapter: SchemaAdapter, fieldAdapter: FieldAdapter, schema: any, value: TType['input'], path: NonNullable<Parameters<Schema['__parse']>[1]>, options: Parameters<Schema['__transformToAdapter']>[0]): Promise<{
        errors: any[];
        parsed: any;
    }>;
    protected __transformToAdapter(_options: {
        args: Omit<ValidationDataBasedOnType<any>, 'withFallback'>;
        force?: boolean;
        toInternalToBubbleUp?: (() => Promise<void>)[];
        schemaAdapter?: SchemaAdapter;
        errorsAsHashedSet?: Set<string>;
        shouldAddStringVersion?: boolean;
        context?: any;
        appendFallbacksBeforeAdapterValidation?: (schema: Schema<any, any>, uniqueNameOfFallback: string, fallbackValidationBeforeAdapter: (adapterToUse: SchemaAdapter, fieldAdapter: FieldAdapter, schema: Omit<Schema<any, any>, '__validateByAdapter'> & {
            __validateByAdapter: Schema<any, any>['__validateByAdapter'];
        }, translatedSchemas: any[], value: any, path: ValidationFallbackCallbackReturnType['errors'][number]['path'], options: Parameters<Schema['__transformToAdapter']>[0]) => ReturnType<Schema['__validateByAdapter']>) => void;
    }): Promise<{
        transformed: ReturnType<FieldAdapter['translate']>;
        asString: string;
    }[]>;
    /** */
    protected __parsersToTransformValue(value: any, parsersToUse?: Set<string>): Promise<any>;
    protected __parse(value: TType['input'], path: ValidationFallbackCallbackReturnType["errors"][number]["path"] | undefined, options: Parameters<Schema['__transformToAdapter']>[0]): Promise<{
        errors: any[];
        parsed: TType['internal'];
    }>;
    /**
     * This let's you refine the schema with custom validations. This is useful when you want to validate something
     * that is not supported by default by the schema adapter.
     *
     * @example
     * ```typescript
     * import * as p from '@palmares/schemas';
     *
     * const numberSchema = p.number().refine((value) => {
     *   if (value < 0) return { code: 'invalid_number', message: 'The number should be greater than 0' };
     * });
     *
     * const { errors, parsed } = await numberSchema.parse(-1);
     *
     * console.log(errors);
     * // [{ isValid: false, code: 'invalid_number', message: 'The number should be greater than 0', path: [] }]
     * ```
     *
     * @param refinementCallback - The callback that will be called to validate the value.
     */
    refine<TRefinementCallback extends (args: {
        value: TType['input'];
        context: TDefinitions['context'];
    }) => Promise<void | undefined | {
        code: string;
        message: string;
    }>>(refinementCallback: TRefinementCallback): Schema<{
        input: TType['input'];
        validate: TType['validate'];
        internal: TType['internal'];
        output: TType['output'];
        representation: TType['representation'];
    }, TDefinitions>;
    /**
     * Allows the value to be either undefined or null.
     *
     * @example
     * ```typescript
     * import * as p from '@palmares/schemas';
     *
     * const numberSchema = p.number().optional();
     *
     * const { errors, parsed } = await numberSchema.parse(undefined);
     *
     * console.log(parsed); // undefined
     *
     * const { errors, parsed } = await numberSchema.parse(null);
     *
     * console.log(parsed); // null
     *
     * const { errors, parsed } = await numberSchema.parse(1);
     *
     * console.log(parsed); // 1
     * ```
     *
     * @returns - The schema we are working with.
     */
    optional(options?: {
        message?: string;
        allow?: false;
    }): Schema<{
        input: TType["input"] | undefined | null;
        validate: TType["validate"] | undefined | null;
        internal: TType["internal"] | undefined | null;
        output: TType["output"] | undefined | null;
        representation: TType["representation"] | undefined | null;
    }, TDefinitions>;
    /**
     * Allows the value to be null and ONLY null. You can also use this function to set a custom message when
     * the value is NULL by setting the { message: 'Your custom message', allow: false } on the options.
     *
     * @example
     * ```typescript
     * import * as p from '@palmares/schemas';
     *
     * const numberSchema = p.number().nullable();
     *
     * const { errors, parsed } = await numberSchema.parse(null);
     *
     * console.log(parsed); // null
     *
     * const { errors, parsed } = await numberSchema.parse(undefined);
     *
     * console.log(errors); // [{ isValid: false, code: 'invalid_type', message: 'Invalid type', path: [] }]
     * ```
     *
     * @param options - The options for the nullable function.
     * @param options.message - The message to be shown when the value is not null. Defaults to 'Cannot be null'.
     * @param options.allow - Whether the value can be null or not. Defaults to true.
     *
     * @returns The schema.
     */
    nullable(options?: {
        message: string;
        allow: false;
    }): Schema<{
        input: TType["input"] | null;
        validate: TType["validate"] | null;
        internal: TType["internal"] | null;
        output: TType["output"] | null;
        representation: TType["representation"] | null;
    }, TDefinitions>;
    /**
     * Appends a custom schema to the schema, this way it will bypass the creation of the schema in runtime.
     *
     * By default when validating, on the first validation we create the schema. Just during the first validation.
     * With this function, you bypass that, so you can speed up the validation process.
     *
     * @example
     * ```typescript
     * import * as p from '@palmares/schemas';
     * import * as z from 'zod';
     *
     * const numberSchema = p.number().appendSchema(z.number());
     *
     * const { errors, parsed } = await numberSchema.parse(1);
     * ```
     *
     * @param schema - The schema to be appended.
     * @param args - The arguments for the schema.
     * @param args.adapter - The adapter to be used. If not provided, the default adapter will be used.
     *
     * @returns The same schema again.
     */
    appendSchema<TType extends any = any>(schema: TDefinitions['schemaAdapter'], args?: {
        adapter?: SchemaAdapter;
    }): Schema<{
        input: TType;
        validate: TType;
        internal: TType;
        output: TType;
        representation: TType;
    }, TDefinitions>;
    /**
     * This method will remove the value from the representation of the schema. If the value is undefined it will keep
     * that way otherwise it will set the value to undefined after it's validated.
     * This is used in conjunction with the {@link data} function, the {@link parse} function or {@link validate}
     * function. This will remove the value from the representation of the schema.
     *
     * By default, the value will be removed just from the representation, in other words, when you call the {@link data}
     * function. But if you want to remove the value from the internal representation, you can pass the argument
     * `toInternal` as true. Then if you still want to remove the value from the representation, you will need to pass
     * the argument `toRepresentation` as true as well.
     *
     * @example
     * ```typescript
     * import * as p from '@palmares/schemas';
     *
     * const userSchema = p.object({
     *   id: p.number().optional(),
     *   name: p.string(),
     *   password: p.string().omit()
     * });
     *
     * const user = await userSchema.data({
     *  id: 1,
     *  name: 'John Doe',
     *  password: '123456'
     * });
     *
     * console.log(user); // { id: 1, name: 'John Doe' }
     * ```
     *
     *
     * @param args - By default, the value will be removed just from the representation, in other words, when you call
     * the {@link data} function.
     * But if you want to remove the value from the internal representation, you can pass the argument `toInternal`
     * as true. Then if you still want to remove the value from the representation, you will need to pass the
     * argument `toRepresentation` as true as well.
     *
     * @returns The schema.
     */
    omit<TToInternal extends boolean, TToRepresentation extends boolean = boolean extends TToInternal ? true : false>(args?: {
        toInternal?: TToInternal;
        toRepresentation?: TToRepresentation;
    }): Schema<{
        input: TToInternal extends true ? TType["input"] | undefined : TType["input"];
        validate: TToInternal extends true ? TType["validate"] | undefined : TType["validate"];
        internal: TToInternal extends true ? undefined : TType["internal"];
        output: TToRepresentation extends true ? TType["output"] | undefined : TType["output"];
        representation: TToRepresentation extends true ? undefined : TType["representation"];
    }, TDefinitions>;
    /**
     * This function is used in conjunction with the {@link validate} function. It's used to save a value to an external
     * source like a database. You should always return the schema after you save the value, that way we will always have
     * the correct type of the schema after the save operation.
     *
     * You can use the {@link toRepresentation} function to transform and clean the value it returns after the save.
     *
     * @example
     * ```typescript
     * import * as p from '@palmares/schemas';
     *
     * import { User } from './models';
     *
     * const userSchema = p.object({
     *   id: p.number().optional(),
     *   name: p.string(),
     *   email: p.string().email(),
     * }).onSave(async (value) => {
     *   // Create or update the user on the database using palmares models or any other library of your choice.
     *   if (value.id)
     *      await User.default.set(value, { search: { id: value.id } });
     *   else
     *      await User.default.set(value);
     *
     *   return value;
     * });
     *
     *
     * // Then, on your controller, do something like this:
     * const { isValid, save, errors } = await userSchema.validate(req.body);
     * if (isValid) {
     *    const savedValue = await save();
     *    return Response.json(savedValue, { status: 201 });
     * }
     *
     * return Response.json({ errors }, { status: 400 });
     * ```
     *
     * @param callback - The callback that will be called to save the value on an external source.
     *
     * @returns The schema.
     */
    onSave<TSave extends ((value: TType['internal']) => (context: unknown) => Promise<TType['output']>) | ((value: TType['internal']) => Promise<TType['output']>)>(callback: TSave): Schema<{
        input: TType['input'];
        validate: TType['validate'];
        internal: TType['internal'];
        output: TType['output'];
        representation: TType['representation'];
    }, Omit<TDefinitions, 'hasSave' | 'context'> & {
        hasSave: true;
        context: ReturnType<TSave> extends (context: any) => any ? Parameters<ReturnType<TSave>>[0] : any;
    }>;
    /**
     * This function is used to validate the schema and save the value to the database. It is used in
     * conjunction with the {@link onSave} function.
     *
     * Different from other validation libraries, palmares schemas is aware that you want to save. On your
     * routes/functions we recommend to ALWAYS use this function instead of {@link parse} directly. This is because
     * this function by default will return an object with the property `save` or the `errors`. If the errors are present,
     * you can return the errors to the user. If the save property is present, you can use to save the value to an
     * external source. e.g. a database.
     *
     * @example
     * ```typescript
     * import * as p from '@palmares/schemas';
     *
     * import { User } from './models';
     *
     * const userSchema = p.object({
     *   id: p.number().optional(),
     *   name: p.string(),
     *   email: p.string().email(),
     * }).onSave(async (value) => {
     *   // Create or update the user on the database using palmares models or any other library of your choice.
     *   if (value.id)
     *      await User.default.set(value, { search: { id: value.id } });
     *   else
     *      await User.default.set(value);
     *
     *   return value;
     * });
     *
     *
     * // Then, on your controller, do something like this:
     * const { isValid, save, errors } = await userSchema.validate(req.body);
     * if (isValid) {
     *    const savedValue = await save();
     *    return Response.json(savedValue, { status: 201 });
     * }
     *
     * return Response.json({ errors }, { status: 400 });
     * ```
     *
     * @param value - The value to be validated.
     *
     * @returns An object with the property isValid, if the value is valid, the function `save` will be present.
     * If the value is invalid, the property errors will be present.
     */
    validate(value: unknown, context?: TDefinitions['context']): Promise<{
        isValid: false;
        errors: any[];
        save: undefined;
    } | {
        isValid: true;
        save: () => Promise<TType['representation']>;
        errors: undefined;
    }>;
    /**
     * Internal function, when we call the {@link validate} function it's this function that gets called
     * when the user uses the `save` function returned by the {@link validate} function if the value is valid.
     *
     * @param value - The value to be saved.
     *
     * @returns The value to representation.
     */
    protected _save(value: TType['input'], context: any): Promise<TType['representation']>;
    /**
     * This function is used to validate and parse the value to the internal representation of the schema.
     *
     * @example
     * ```typescript
     * import * as p from '@palmares/schemas';
     *
     * const numberSchema = p.number().allowString();
     *
     * const { errors, parsed } = await numberSchema.parse('123');
     *
     * console.log(parsed); // 123
     * ```
     *
     * @param value - The value to be parsed.
     *
     * @returns The parsed value.
     */
    parse(value: unknown): Promise<{
        errors?: any[];
        parsed: TType['internal'];
    }>;
    /**
     * This function is used to transform the value to the representation without validating it.
     * This is useful when you want to return a data from a query directly to the user. But for example
     * you are returning the data of a user, you can clean the password or any other sensitive data.
     *
     * @example
     * ```typescript
     * import * as p from '@palmares/schemas';
     *
     * const userSchema = p.object({
     *   id: p.number().optional(),
     *   name: p.string(),
     *   email: p.string().email(),
     *   password: p.string().optional()
     * }).toRepresentation(async (value) => {
     *   return {
     *    id: value.id,
     *    name: value.name,
     *   email: value.email
     *  }
     * });
     *
     * const user = await userSchema.data({
     *   id: 1,
     *   name: 'John Doe',
     *   email: 'john@gmail.com',
     *   password: '123456'
     * });
     * ```
     */
    data(value: TType['output']): Promise<TType['representation']>;
    instanceOf(args: Schema['__type']): Schema<{
        input: TType["input"];
        validate: TType["validate"];
        internal: TType["internal"];
        output: TType["output"];
        representation: TType["representation"];
    }, TDefinitions>;
    /**
     * This function is used to add a default value to the schema. If the value is either undefined or null,
     * the default value will be used.
     *
     * @example
     * ```typescript
     * import * as p from '@palmares/schemas';
     *
     * const numberSchema = p.number().default(0);
     *
     * const { errors, parsed } = await numberSchema.parse(undefined);
     *
     * console.log(parsed); // 0
     * ```
     */
    default<TDefaultValue extends TType['input'] | (() => Promise<TType['input']>)>(defaultValueOrFunction: TDefaultValue): Schema<{
        input: TType["input"] | undefined | null;
        validate: TType["validate"];
        internal: TType["internal"];
        output: TType["output"] | undefined | null;
        representation: TType["representation"];
    }, TDefinitions>;
    /**
     * This function let's you customize the schema your own way. After we translate the schema on the adapter we call
     * this function to let you customize the custom schema your own way. Our API does not support passthrough?
     * No problem, you can use this function to customize the zod schema.
     *
     * @example
     * ```typescript
     * import * as p from '@palmares/schemas';
     *
     * const numberSchema = p.number().extends((schema) => {
     *   return schema.nonnegative();
     * });
     *
     * const { errors, parsed } = await numberSchema.parse(-1);
     *
     * console.log(errors);
     * // [{ isValid: false, code: 'nonnegative', message: 'The number should be nonnegative', path: [] }]
     * ```
     *
     * @param callback - The callback that will be called to customize the schema.
     * @param toStringCallback - The callback that will be called to transform the schema to a string when you want
     * to compile the underlying schema to a string so you can save it for future runs.
     *
     * @returns The schema.
     */
    extends(callback: (schema: Awaited<ReturnType<NonNullable<TDefinitions['schemaAdapter'][TDefinitions['schemaType']]>['translate']>>) => Awaited<ReturnType<NonNullable<TDefinitions['schemaAdapter'][TDefinitions['schemaType']]>['translate']>>, toStringCallback?: (schemaAsString: string) => string): this;
    /**
     * This function is used to transform the value to the representation of the schema. When using the {@link data}
     * function. With this function you have full control to add data cleaning for example, transforming the data
     * and whatever. Another use case is when you want to return deeply nested recursive data.
     * The schema maps to itself.
     *
     * @example
     * ```typescript
     * import * as p from '@palmares/schemas';
     *
     * const recursiveSchema = p.object({
     *   id: p.number().optional(),
     *   name: p.string(),
     * }).toRepresentation(async (value) => {
     *    return {
     *      id: value.id,
     *      name: value.name,
     *      children: await Promise.all(value.children.map(async (child) => await recursiveSchema.data(child)))
     *    }
     * });
     *
     * const data = await recursiveSchema.data({
     *    id: 1,
     *    name: 'John Doe',
     * });
     * ```
     *
     * @example
     * ```
     * import * as p from '@palmares/schemas';
     *
     * const colorToRGBSchema = p.string().toRepresentation(async (value) => {
     *    switch (value) {
     *      case 'red': return { r: 255, g: 0, b: 0 };
     *      case 'green': return { r: 0, g: 255, b: 0 };
     *      case 'blue': return { r: 0, g: 0, b: 255 };
     *      default: return { r: 0, g: 0, b: 0 };
     *   }
     * });
     * ```
     * @param toRepresentationCallback - The callback that will be called to transform the value to the representation.
     * @param options - Options for the toRepresentation function.
     * @param options.after - Whether the toRepresentationCallback should be called after the existing
     * toRepresentationCallback. Defaults to true.
     * @param options.before - Whether the toRepresentationCallback should be called before the existing
     * toRepresentationCallback. Defaults to true.
     *
     * @returns The schema with a new return type
     */
    toRepresentation<TRepresentation>(toRepresentationCallback: (value: TType['representation']) => Promise<TRepresentation> | TRepresentation, options?: {
        after?: boolean;
        before?: boolean;
    }): Schema<{
        input: TType["input"];
        validate: TType["validate"];
        internal: TType["internal"];
        output: TType["output"];
        representation: TRepresentation;
    }, TDefinitions>;
    /**
     * This function is used to transform the value to the internal representation of the schema. This is useful
     * when you want to transform the value to a type that the schema adapter can understand. For example, you
     * might want to transform a string to a date. This is the function you use.
     *
     * @example
     * ```typescript
     * import * as p from '@palmares/schemas';
     *
     * const dateSchema = p.string().toInternal((value) => {
     *   return new Date(value);
     * });
     *
     * const date = await dateSchema.parse('2021-01-01');
     *
     * console.log(date); // Date object
     *
     * const rgbToColorSchema = p.object({
     *   r: p.number().min(0).max(255),
     *   g: p.number().min(0).max(255),
     *   b: p.number().min(0).max(255),
     * }).toInternal(async (value) => {
     *    if (value.r === 255 && value.g === 0 && value.b === 0) return 'red';
     *    if (value.r === 0 && value.g === 255 && value.b === 0) return 'green';
     *    if (value.r === 0 && value.g === 0 && value.b === 255) return 'blue';
     *    return `rgb(${value.r}, ${value.g}, ${value.b})`;
     * });
     * ```
     *
     * @param toInternalCallback - The callback that will be called to transform the value to the internal representation.
     *
     * @returns The schema with a new return type.
     */
    toInternal<TInternal>(toInternalCallback: (value: TType['validate']) => Promise<TInternal>): Schema<{
        input: TType["input"];
        validate: TType["validate"];
        internal: TInternal;
        output: TType["output"];
        representation: TType["representation"];
    }, TDefinitions>;
    /**
     * Called before the validation of the schema. Let's say that you want to validate a date that might receive a
     * string, you can convert that string to a date here BEFORE the validation. This pretty much transforms the value
     * to a type that the schema adapter can understand.
     *
     * @example
     * ```
     * import * as p from '@palmares/schemas';
     * import * as z from 'zod';
     *
     * const customRecordToMapSchema = p.schema().appendSchema(z.map()).toValidate(async (value) => {
     *    return new Map(value); // Before validating we transform the value to a map.
     * });
     *
     * const { errors, parsed } = await customRecordToMapSchema.parse({ key: 'value' });
     * ```
     *
     * @param toValidateCallback - The callback that will be called to validate the value.
     *
     * @returns The schema with a new return type.
     */
    toValidate<TValidate>(toValidateCallback: (value: TType['input'], context: TDefinitions['context']) => Promise<TValidate> | TValidate): Schema<{
        input: TType["input"];
        validate: TValidate;
        internal: TType["internal"];
        output: TType["output"];
        representation: TType["representation"];
    }, TDefinitions>;
    /**
     * Used to transform the given schema on a stringfied version of the adapter.
     */
    compile(adapter: SchemaAdapter): Promise<string[]>;
    static new<TType extends {
        input: any;
        output: any;
        internal: any;
        representation: any;
        validate: any;
    }, TDefinitions extends DefinitionsOfSchemaType = DefinitionsOfSchemaType<SchemaAdapter & Palmares.PSchemaAdapter>>(..._args: any[]): Schema<TType, TDefinitions>;
}
export declare const schema: typeof Schema.new;
//# sourceMappingURL=schema.d.ts.map