import * as _angular_core from '@angular/core';
import { Signal, AfterContentInit, OnDestroy, InputSignal, AfterViewInit, InjectionToken } from '@angular/core';
import { ValidationErrors, NgModel, NgModelGroup, AsyncValidator, AbstractControl, NgForm, AsyncValidatorFn, FormsModule, ControlContainer, FormGroup } from '@angular/forms';
import { Observable } from 'rxjs';
import * as ngx_vest_forms from 'ngx-vest-forms';
import { StaticSuite } from 'vest';

/**
 * Angular's ValidationErrors is `{ [key: string]: any }`.
 * We extend it with Vest-specific structure for better type safety.
 */
type VestValidationErrors = {
    /** Vest error messages array */
    errors?: readonly string[];
    /** Vest warning messages array */
    warnings?: readonly string[];
} & ValidationErrors;
/**
 * Form control status values as defined by Angular.
 * @see AbstractControl.status
 */
type FormControlStatus = 'VALID' | 'INVALID' | 'PENDING' | 'DISABLED';
/**
 * Represents the core state of an Angular form control.
 * Uses narrower types than Angular's defaults where possible.
 */
type FormControlState = {
    readonly status: FormControlStatus | null;
    readonly isValid: boolean;
    readonly isInvalid: boolean;
    readonly isPending: boolean;
    readonly isDisabled: boolean;
    readonly isTouched: boolean;
    readonly isDirty: boolean;
    readonly isPristine: boolean;
    /** Errors from Angular validators or Vest validation */
    readonly errors: VestValidationErrors | null;
};
declare class FormControlStateDirective {
    #private;
    protected readonly contentNgModel: _angular_core.Signal<NgModel | undefined>;
    protected readonly contentNgModelGroup: _angular_core.Signal<NgModelGroup | undefined>;
    constructor();
    /**
     * Main control state computed signal (merges robust touched/dirty)
     */
    readonly controlState: _angular_core.Signal<FormControlState>;
    /**
     * Extracts error messages from Angular/Vest errors (recursively flattens)
     */
    readonly errorMessages: _angular_core.Signal<string[]>;
    /**
     * ADVANCED: updateOn strategy (change/blur/submit) if available
     */
    readonly updateOn: _angular_core.Signal<"change" | "blur" | "submit">;
    /**
     * ADVANCED: Composite/derived signals for advanced error display logic
     */
    readonly isValidTouched: _angular_core.Signal<boolean>;
    readonly isInvalidTouched: _angular_core.Signal<boolean>;
    readonly shouldShowErrors: _angular_core.Signal<boolean>;
    /**
     * Extracts warning messages from Vest validation results.
     * Checks two sources:
     * 1. control.errors.warnings (when errors exist alongside warnings)
     * 2. FormDirective.fieldWarnings (for warnings-only scenarios)
     * This dual-source approach allows warnings to be displayed without affecting field validity.
     */
    readonly warningMessages: _angular_core.Signal<string[]>;
    /**
     * Whether async validation is in progress
     */
    readonly hasPendingValidation: _angular_core.Signal<boolean>;
    /**
     * Convenience signals for common state checks
     */
    readonly isValid: _angular_core.Signal<boolean>;
    readonly isInvalid: _angular_core.Signal<boolean>;
    readonly isPending: _angular_core.Signal<boolean>;
    readonly isTouched: _angular_core.Signal<boolean>;
    readonly isDirty: _angular_core.Signal<boolean>;
    readonly isPristine: _angular_core.Signal<boolean>;
    readonly isDisabled: _angular_core.Signal<boolean>;
    readonly hasErrors: _angular_core.Signal<boolean>;
    /**
     * Whether this control has been validated at least once.
     * True after the first validation completes, even if the user hasn't touched the field.
     * This is primarily used for warning display and other derived state that should react
     * to validationConfig-triggered validation even before the user touches the field.
     */
    readonly hasBeenValidated: _angular_core.Signal<boolean>;
    static ɵfac: _angular_core.ɵɵFactoryDeclaration<FormControlStateDirective, never>;
    static ɵdir: _angular_core.ɵɵDirectiveDeclaration<FormControlStateDirective, "[formControlState], [ngxControlState]", ["formControlState", "ngxControlState"], {}, {}, ["contentNgModel", "contentNgModelGroup"], never, true, never>;
}

/**
 * Error display modes for form controls.
 * - 'on-blur': Show errors after field is touched/blurred
 * - 'on-submit': Show errors after form submission
 * - 'on-blur-or-submit': Show errors after blur or form submission (default)
 * - 'on-dirty': Show errors as soon as the field value changes
 * - 'always': Show errors immediately, even on pristine fields
 */
type ScErrorDisplayMode = 'on-blur' | 'on-submit' | 'on-blur-or-submit' | 'on-dirty' | 'always';
/**
 * Warning display modes for form controls.
 * - 'on-touch': Show warnings after field is touched/blurred
 * - 'on-validated-or-touch': Show warnings after validation runs or field is touched (default)
 * - 'on-dirty': Show warnings as soon as the field value changes
 * - 'always': Show warnings immediately, even on pristine fields
 */
type NgxWarningDisplayMode = 'on-touch' | 'on-validated-or-touch' | 'on-dirty' | 'always';
declare class FormErrorDisplayDirective {
    #private;
    /**
     * Input signal for error display mode.
     * Works seamlessly with hostDirectives in Angular 19+.
     */
    readonly errorDisplayMode: _angular_core.InputSignal<ScErrorDisplayMode>;
    /**
     * Input signal for warning display mode.
     * Controls whether warnings are shown only after touch or also after validation.
     */
    readonly warningDisplayMode: _angular_core.InputSignal<NgxWarningDisplayMode>;
    readonly controlState: Signal<FormControlState>;
    readonly errorMessages: Signal<string[]>;
    readonly warningMessages: Signal<string[]>;
    readonly hasPendingValidation: Signal<boolean>;
    readonly isTouched: Signal<boolean>;
    readonly isDirty: Signal<boolean>;
    readonly isValid: Signal<boolean>;
    readonly isInvalid: Signal<boolean>;
    readonly hasBeenValidated: Signal<boolean>;
    /**
     * Expose updateOn and formSubmitted as public signals for advanced consumers.
     * updateOn: The ngModelOptions.updateOn value for the control (change/blur/submit)
     * formSubmitted: true after the form is submitted (if NgForm is present)
     */
    readonly updateOn: Signal<"change" | "blur" | "submit">;
    /**
     * Signal that tracks NgForm.submitted state reactively.
     *
     * Map form-level submit/reset events directly to boolean state.
     *
     * This keeps programmatic `NgForm.onSubmit()` reactive in zoneless mode and
     * avoids depending on `NgForm.submitted`, whose getter intentionally reads an
     * internal signal with `untracked()`.
     *
     * Note: when this directive is used outside an `NgForm` (no parent form), no
     * subscription is wired up and this signal stays `false` for the lifetime of
     * the directive. Consumers relying on submitted state must host the field
     * inside an `NgForm` (or `ngxVestForm`).
     */
    readonly formSubmitted: Signal<boolean>;
    constructor();
    /**
     * Determines if errors should be shown based on the specified display mode
     * and the control's state (touched/submitted/dirty).
     *
     * Note: We check both hasErrors (extracted error messages) AND isInvalid (Angular's validation state)
     * because in some cases (like conditional validations via validationConfig), the control is marked
     * as invalid by Angular before error messages are extracted from Vest. This ensures aria-invalid
     * is set correctly even during the validation propagation delay.
     *
     * For validationConfig-triggered validations, a field may become invalid before it has been
     * touched. Error visibility still respects the field's own `errorDisplayMode`, so untouched
     * dependent fields can remain visually quiet until blur or submit.
     */
    readonly shouldShowErrors: Signal<boolean>;
    /**
     * Errors to display (filtered for pending state)
     */
    readonly errors: Signal<string[]>;
    /**
     * Warnings to display (filtered for pending state)
     */
    readonly warnings: Signal<string[]>;
    /**
     * Whether the control is currently being validated (pending)
     * Excludes pristine+untouched controls to prevent "Validating..." on initial load
     */
    readonly isPending: Signal<boolean>;
    /**
     * Determines if warnings should be shown based on the specified display mode
     * and the control's state (touched/validated/dirty).
     *
     * NOTE: Unlike errors, warnings can exist on VALID fields (warnings-only scenario).
     * We don't require isInvalid() because Vest warn() tests don't affect field validity.
     *
     * UX Note: We include `hasBeenValidated` for `on-validated-or-touch` mode to support
     * cross-field validation. If Field A triggers validation on Field B (via validationConfig),
     * Field B should show warnings if it has them, even if the user hasn't touched Field B yet.
     * Unlike errors (which block submission), warnings are informational and safe to show.
     */
    readonly shouldShowWarnings: Signal<boolean>;
    static ɵfac: _angular_core.ɵɵFactoryDeclaration<FormErrorDisplayDirective, never>;
    static ɵdir: _angular_core.ɵɵDirectiveDeclaration<FormErrorDisplayDirective, "[formErrorDisplay], [ngxErrorDisplay]", ["formErrorDisplay", "ngxErrorDisplay"], { "errorDisplayMode": { "alias": "errorDisplayMode"; "required": false; "isSignal": true; }; "warningDisplayMode": { "alias": "warningDisplayMode"; "required": false; "isSignal": true; }; }, {}, never, never, true, [{ directive: typeof FormControlStateDirective; inputs: {}; outputs: {}; }]>;
}

type AriaAssociationMode = 'all-controls' | 'single-control' | 'none';
/**
 * Splits an `aria-describedby` attribute value into normalized token IDs.
 */
declare function parseAriaIdTokens(value: string | null): string[];
/**
 * Merges currently-active wrapper IDs into an existing `aria-describedby` value.
 *
 * Existing tokens owned by the wrapper are removed first, then current active IDs
 * are appended while preserving non-owned tokens and token uniqueness.
 */
declare function mergeAriaDescribedBy(existing: string | null, activeIds: readonly string[], ownedIds: readonly string[]): string | null;
/**
 * Resolves control targets based on ARIA association mode.
 */
declare function resolveAssociationTargets(controls: readonly HTMLElement[], mode: AriaAssociationMode): HTMLElement[];

/**
 * Accessible form control wrapper built with WCAG 2.2 AA considerations.
 *
 * Wrap form fields to automatically display validation errors, warnings, and pending states
 * with proper accessibility attributes.
 *
 * @usageNotes
 *
 * ### Basic Usage
 * ```html
 * <ngx-control-wrapper>
 *   <label for="email">Email</label>
 *   <input id="email" name="email" [ngModel]="formValue().email" />
 * </ngx-control-wrapper>
 * ```
 *
 * ### Error Display Modes
 * Control when errors appear using the `errorDisplayMode` input:
 * - `'on-blur-or-submit'` (default): Show errors after blur OR form submit
 * - `'on-blur'`: Show errors only after blur
 * - `'on-submit'`: Show errors only after form submit
 * - `'on-dirty'`: Show errors as soon as the field value changes
 * - `'always'`: Show errors immediately, even on pristine fields
 *
 * ```html
 * <ngx-control-wrapper [errorDisplayMode]="'on-submit'">
 *   <input name="email" [ngModel]="formValue().email" />
 * </ngx-control-wrapper>
 * ```
 *
 * ### Warning Display Modes
 * Control when warnings appear using the `warningDisplayMode` input:
 * - `'on-validated-or-touch'` (default): Show warnings after validation runs or touch
 * - `'on-touch'`: Show warnings only after touch/blur
 * - `'on-dirty'`: Show warnings as soon as the field value changes
 * - `'always'`: Show warnings immediately, even on pristine fields
 *
 * ```html
 * <ngx-control-wrapper [warningDisplayMode]="'on-dirty'">
 *   <input name="email" [ngModel]="formValue().email" />
 * </ngx-control-wrapper>
 * ```
 *
 * ### Accessibility Features (Automatic)
 * - Unique IDs for error/warning/pending regions
 * - `aria-describedby` linking errors to form controls
 * - `aria-invalid="true"` when errors are shown
 * - Uses `role="status"` with `aria-live="polite"` for non-disruptive announcements
 * - Debounced pending state to prevent flashing for quick validations
 *
 * ### WCAG 2.2 AA - Error Severity Levels
 * This component uses `role="status"` for **field-level** validation messages:
 * - **Errors**: Non-disruptive announcement (user can continue filling other fields)
 * - **Warnings**: Informational only, doesn't block submission
 * - **Pending**: Status update while async validation runs
 *
 * For **form-level blocking errors** (e.g., submission failed), implement a separate
 * error summary component with `role="alert"` and `aria-live="assertive"`.
 *
 * @see {@link FormErrorDisplayDirective} for custom wrapper implementation
 *
 *   Form-Level Blocking Errors:
 *   - For post-submit validation errors that block submission, implement a separate
 *     form-level error summary with role="alert" and aria-live="assertive"
 *   - This component uses role="status" for field-level errors (non-disruptive)
 *   - Example for form-level errors:
 *     ```html
 *     <!-- Keep in DOM; update text content on submit -->
 *     <div id="form-errors" role="alert" aria-live="assertive" aria-atomic="true"></div>
 *     ```
 *   - This separation provides immediate announcements for blocking form errors while
 *     keeping inline field errors non-disruptive. Follows WCAG ARIA21/ARIA22 guidance.
 *
 * Error & Warning Display Behavior:
 *   - The error display mode can be configured globally using the NGX_ERROR_DISPLAY_MODE_TOKEN injection token, or per instance using the `errorDisplayMode` input on FormErrorDisplayDirective (which this component uses as a hostDirective).
 *   - Possible error display values: 'on-blur' | 'on-submit' | 'on-blur-or-submit' | 'on-dirty' | 'always' (default: 'on-blur-or-submit')
 *   - The warning display mode can be configured globally using NGX_WARNING_DISPLAY_MODE_TOKEN, or per instance using the `warningDisplayMode` input on FormErrorDisplayDirective.
 *   - Possible warning display values: 'on-touch' | 'on-validated-or-touch' | 'on-dirty' | 'always' (default: 'on-validated-or-touch')
 *
 * Example (per instance):
 *   <div ngxControlWrapper>
 *     <label>
 *       <span>First name</span>
 *       <input type="text" name="firstName" [ngModel]="formValue().firstName" />
 *     </label>
 *   </div>
 *   /// To customize errorDisplayMode for this instance, use the errorDisplayMode input.
 *
 * Example (with warnings and pending):
 *   <ngx-control-wrapper>
 *     <input name="username" ngModel />
 *   </ngx-control-wrapper>
 *   /// If async validation is running for >500ms, a spinner and 'Validating…' will be shown.
 *   /// Once shown, the validation message stays visible for minimum 500ms to prevent flashing.
 *   /// If Vest warnings are present, they will be shown below errors.
 *
 * Example (global config):
 *   import { provide } from '@angular/core';
 *   import { NGX_ERROR_DISPLAY_MODE_TOKEN } from 'ngx-vest-forms';
 *   @Component({
 *     providers: [
 *       provide(NGX_ERROR_DISPLAY_MODE_TOKEN, { useValue: 'on-submit' })
 *     ]
 *   })
 *   export class MyComponent {}
 *
 * Best Practices:
 *   - Use for single-control wrappers.
 *   - For multi-control/group containers, prefer `ngx-form-group-wrapper`.
 *   - Do not manually display errors for individual fields; rely on this wrapper.
 *   - Validate with tools like Accessibility Insights and real screen reader testing.
 *
 * @see https://www.w3.org/WAI/WCAG22/Techniques/aria/ARIA19 - ARIA19: Using ARIA role=alert
 * @see https://www.w3.org/WAI/WCAG22/Techniques/aria/ARIA22 - ARIA22: Using role=status
 */
declare class ControlWrapperComponent implements AfterContentInit, OnDestroy {
    protected readonly errorDisplay: FormErrorDisplayDirective;
    private readonly elementRef;
    private readonly destroyRef;
    /**
     * Controls how this wrapper applies ARIA attributes to descendant controls.
     *
     * - `all-controls` (default, backwards compatible): apply `aria-describedby` / `aria-invalid`
     *   to all `input/select/textarea` elements inside the wrapper.
     * - `single-control`: apply ARIA attributes only when exactly one control is found.
     *   (Useful for wrappers that sometimes contain helper buttons/controls.)
     * - `none`: do not mutate descendant controls at all (group-safe mode).
     *
     * Notes:
     * - Use `none` when wrapping a container (e.g. `NgModelGroup`) to avoid stamping ARIA
     *   across multiple child controls.
     * - This does not affect whether messages render; it only affects ARIA wiring.
     */
    readonly ariaAssociationMode: _angular_core.InputSignal<AriaAssociationMode>;
    protected readonly uniqueId: string;
    protected readonly errorId: string;
    protected readonly warningId: string;
    protected readonly pendingId: string;
    private readonly formControls;
    private readonly contentInitialized;
    private mutationObserver;
    /**
     * Debounced pending state to prevent flashing for quick async validations.
     * Uses createDebouncedPendingState utility with 500ms delay and 500ms minimum display.
     */
    private readonly pendingState;
    protected readonly showPendingMessage: _angular_core.Signal<boolean>;
    /**
     * Whether to display warnings.
     * Delegates to FormErrorDisplayDirective's centralized shouldShowWarnings signal.
     *
     * This ensures consistent warning display behavior across all form components
     * and supports the new 'on-dirty' and 'always' display modes.
     */
    protected readonly shouldShowWarnings: _angular_core.Signal<boolean>;
    /**
     * Computed signal that builds aria-describedby string based on visible regions
     */
    protected readonly ariaDescribedBy: _angular_core.Signal<string | null>;
    /**
     * IDs managed by this wrapper when composing aria-describedby.
     *
     * We remove only these from the consumer-provided aria-describedby tokens and then
     * append the currently-relevant wrapper IDs. This prevents clobbering app-provided
     * hint/help text associations.
     */
    private readonly wrapperOwnedDescribedByIds;
    constructor();
    ngAfterContentInit(): void;
    ngOnDestroy(): void;
    /**
     * Query and update the list of form controls within this wrapper.
     * Called on init and whenever the DOM structure changes.
     */
    private updateFormControls;
    static ɵfac: _angular_core.ɵɵFactoryDeclaration<ControlWrapperComponent, never>;
    static ɵcmp: _angular_core.ɵɵComponentDeclaration<ControlWrapperComponent, "ngx-control-wrapper, sc-control-wrapper, [scControlWrapper], [ngxControlWrapper], [ngx-control-wrapper], [sc-control-wrapper]", never, { "ariaAssociationMode": { "alias": "ariaAssociationMode"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, [{ directive: typeof FormErrorDisplayDirective; inputs: { "errorDisplayMode": "errorDisplayMode"; "warningDisplayMode": "warningDisplayMode"; }; outputs: {}; }]>;
}

/**
 * Group-safe wrapper for `NgModelGroup` containers.
 *
 * This component renders group-level errors/warnings/pending UI, but intentionally
 * does **not** stamp `aria-describedby` / `aria-invalid` onto descendant controls.
 *
 * Use this when you want a wrapper around a container that has multiple inputs.
 * For single inputs, prefer `<ngx-control-wrapper>`.
 */
declare class FormGroupWrapperComponent {
    protected readonly errorDisplay: FormErrorDisplayDirective;
    /**
     * Controls the debounce behavior for the pending message.
     * Defaults are conservative to avoid flashing.
     */
    readonly pendingDebounce: _angular_core.InputSignal<{
        showAfter: number;
        minimumDisplay: number;
    }>;
    protected readonly uniqueId: string;
    readonly errorId: string;
    readonly warningId: string;
    readonly pendingId: string;
    private readonly pendingState;
    protected readonly showPendingMessage: _angular_core.Signal<boolean>;
    /**
     * Helpful if consumers want to wire aria-describedby manually (e.g. fieldset/legend pattern).
     */
    readonly describedByIds: _angular_core.Signal<string | null>;
    static ɵfac: _angular_core.ɵɵFactoryDeclaration<FormGroupWrapperComponent, never>;
    static ɵcmp: _angular_core.ɵɵComponentDeclaration<FormGroupWrapperComponent, "ngx-form-group-wrapper, sc-form-group-wrapper, [ngxFormGroupWrapper], [scFormGroupWrapper]", ["ngxFormGroupWrapper"], { "pendingDebounce": { "alias": "pendingDebounce"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, [{ directive: typeof FormErrorDisplayDirective; inputs: { "errorDisplayMode": "errorDisplayMode"; "warningDisplayMode": "warningDisplayMode"; }; outputs: {}; }]>;
}

/**
 * Wires a control container to its error/warning/pending regions.
 *
 * This directive is intended for custom wrappers/components.
 * It composes `FormErrorDisplayDirective` (and thus `FormControlStateDirective`)
 * and applies `aria-invalid` / `aria-describedby` to descendant controls.
 *
 * It does not render any UI; you can use the generated IDs to render messages.
 */
declare class FormErrorControlDirective implements AfterContentInit, OnDestroy {
    protected readonly errorDisplay: FormErrorDisplayDirective;
    private readonly elementRef;
    /**
     * Controls how this directive applies ARIA attributes to descendant controls.
     *
     * - `all-controls` (default): apply ARIA attributes to all input/select/textarea descendants.
     * - `single-control`: apply ARIA attributes only when exactly one control is found.
     * - `none`: do not mutate descendant controls.
     */
    readonly ariaAssociationMode: _angular_core.InputSignal<AriaAssociationMode>;
    /**
     * Unique ID prefix for this instance.
     * Use these IDs to render message regions and to support aria-describedby.
     */
    protected readonly uniqueId: string;
    readonly errorId: string;
    readonly warningId: string;
    readonly pendingId: string;
    private readonly formControls;
    private readonly contentInitialized;
    private mutationObserver;
    private readonly pendingState;
    readonly showPendingMessage: _angular_core.Signal<boolean>;
    /**
     * aria-describedby value representing the *currently relevant* message regions.
     */
    readonly ariaDescribedBy: _angular_core.Signal<string | null>;
    private readonly ownedDescribedByIds;
    constructor();
    ngAfterContentInit(): void;
    ngOnDestroy(): void;
    private updateFormControls;
    static ɵfac: _angular_core.ɵɵFactoryDeclaration<FormErrorControlDirective, never>;
    static ɵdir: _angular_core.ɵɵDirectiveDeclaration<FormErrorControlDirective, "[formErrorControl], [ngxErrorControl]", ["formErrorControl", "ngxErrorControl"], { "ariaAssociationMode": { "alias": "ariaAssociationMode"; "required": false; "isSignal": true; }; }, {}, never, never, true, [{ directive: typeof FormErrorDisplayDirective; inputs: { "errorDisplayMode": "errorDisplayMode"; "warningDisplayMode": "warningDisplayMode"; }; outputs: {}; }]>;
}

/**
 * Validation Options
 */
type ValidationOptions = {
    /**
     * debounceTime for the next validation
     */
    debounceTime: number;
};

/**
 * Hooks into `ngModelGroup`/`ngxModelGroup` and runs async group-level validation
 * through the parent `FormDirective` Vest suite bridge.
 */
declare class FormModelGroupDirective implements AsyncValidator {
    /**
     * Per-group async validation options.
     *
     * Defaults to no debounce (`{ debounceTime: 0 }`).
     */
    validationOptions: _angular_core.InputSignal<ValidationOptions>;
    private readonly formDirective;
    /**
     * Runs async validation for the current model-group control.
     *
     * Returns `null` (fail-open) when used outside an `ngxVestForm` context.
     */
    validate(control: AbstractControl): Observable<ValidationErrors | null>;
    static ɵfac: _angular_core.ɵɵFactoryDeclaration<FormModelGroupDirective, never>;
    static ɵdir: _angular_core.ɵɵDirectiveDeclaration<FormModelGroupDirective, "[ngModelGroup],[ngxModelGroup]", never, { "validationOptions": { "alias": "validationOptions"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
}

/**
 * Hooks into `ngModel`/`ngxModel` and runs async field-level validation
 * through the parent `FormDirective` Vest suite bridge.
 */
declare class FormModelDirective implements AsyncValidator {
    /**
     * Per-control async validation options.
     *
     * Defaults to no debounce (`{ debounceTime: 0 }`).
     */
    validationOptions: _angular_core.InputSignal<ValidationOptions>;
    /**
     * Reference to the form that needs to be validated
     * Injected optionally so that using ngModel outside of an ngxVestForm
     * does not crash the application. In that case, validation becomes a no-op.
     */
    private readonly formDirective;
    /**
     * Runs field-level async validation for this control.
     *
     * Returns `null` (fail-open) when used outside an `ngxVestForm` context.
     */
    validate(control: AbstractControl): Observable<ValidationErrors | null>;
    static ɵfac: _angular_core.ɵɵFactoryDeclaration<FormModelDirective, never>;
    static ɵdir: _angular_core.ɵɵDirectiveDeclaration<FormModelDirective, "[ngModel],[ngxModel]", never, { "validationOptions": { "alias": "validationOptions"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
}

declare const ROOT_FORM = "rootForm";

/**
 * Primitive types that should not be traversed for nested paths
 */
type Primitive = string | number | boolean | Date | null | undefined;
/**
 * Helper type to extract the element type from an array.
 * Used internally for type inference with array paths.
 *
 * @template T - The array type to extract from
 * @example
 * ```typescript
 * type Element = ArrayElement<string[]>; // Result: string
 * type Element2 = ArrayElement<NotArray>; // Result: never
 * ```
 */
/**
 * Recursively generates all valid field paths for a type as string literals.
 * This provides full IDE autocomplete and compile-time validation for field names.
 *
 * **Key Features:**
 * - Supports nested objects with dot notation (e.g., 'user.address.city')
 * - Works with optional properties from DeepPartial types
 * - Handles arrays and readonly arrays
 * - Stops recursion at primitive types
 * - Maximum depth of 10 levels to prevent infinite recursion
 *
 * **Type Safety Benefits:**
 * - IDE autocomplete for all valid field paths
 * - Compile-time errors for typos in field names
 * - Refactoring support (rename property → all usages update)
 * - Self-documenting code through type inference
 *
 * @template T - The model type to extract field paths from
 * @template Prefix - Internal recursion prefix (do not use directly)
 * @template Depth - Internal depth counter to prevent infinite recursion
 *
 * @example
 * ```typescript
 * type Model = {
 *   name: string;
 *   profile: {
 *     age: number;
 *     address: {
 *       city: string;
 *     }
 *   }
 * };
 *
 * type Paths = FieldPath<Model>;
 * /// Result: 'name' | 'profile' | 'profile.age' | 'profile.address' | 'profile.address.city'
 * ```
 *
 * @example With DeepPartial
 * ```typescript
 * type FormModel = DeepPartial<{
 *   user: {
 *     email: string;
 *     phone: string;
 *   }
 * }>;
 *
 * type Paths = FieldPath<FormModel>;
 * /// Result: 'user' | 'user.email' | 'user.phone'
 * ```
 */
type FieldPath<T, Prefix extends string = '', Depth extends readonly number[] = []> = Depth['length'] extends 10 ? never : T extends Primitive ? never : T extends ReadonlyArray<infer U> ? FieldPath<U, Prefix, [...Depth, 1]> : {
    [K in keyof T & string]: T[K] extends Primitive ? `${Prefix}${K}` : T[K] extends ReadonlyArray<infer U> ? `${Prefix}${K}` | (U extends Primitive ? never : FieldPath<U, `${Prefix}${K}.`, [...Depth, 1]>) : `${Prefix}${K}` | FieldPath<T[K], `${Prefix}${K}.`, [...Depth, 1]>;
}[keyof T & string];
/**
 * Type-safe validation configuration map.
 * Maps trigger field paths to arrays of dependent field paths that should be revalidated.
 *
 * **Use Case:**
 * Define which fields should trigger validation of other fields when they change.
 * For example, when password changes, confirmPassword should be revalidated.
 *
 * **Type Safety:**
 * - All keys must be valid field paths from the model
 * - All dependent field paths must be valid
 * - IDE autocomplete works for both keys and values
 * - Compile-time errors for invalid field references
 *
 * @template T - The form model type
 *
 * @example
 * ```typescript
 * type FormModel = DeepPartial<{
 *   password: string;
 *   confirmPassword: string;
 *   addresses: {
 *     billing: { city: string; }
 *   }
 * }>;
 *
 * /// ✅ Type-safe configuration with autocomplete
 * const config: ValidationConfigMap<FormModel> = {
 *   password: ['confirmPassword'],
 *   'addresses.billing.city': ['password'],
 * };
 *
 * /// ❌ TypeScript error - invalid field name
 * const badConfig: ValidationConfigMap<FormModel> = {
 *   passwordd: ['confirmPassword'], // Typo caught at compile time
 * };
 * ```
 */
type ValidationConfigMap<T> = Partial<Record<FieldPath<T>, Array<FieldPath<T>>>>;
/**
 * Type-safe field name for use in Vest test() calls and form APIs.
 * Combines valid field paths with the special ROOT_FORM constant.
 *
 * **Use Case:**
 * When defining Vest test() calls, use this type for the field parameter
 * to get autocomplete and type safety.
 *
 * **Special Values:**
 * - Any valid FieldPath from the model
 * - ROOT_FORM constant for form-level validations
 *
 * @template T - The form model type
 *
 * @example
 * ```typescript
 * import { ROOT_FORM } from 'ngx-vest-forms';
 *
 * type FormModel = DeepPartial<{
 *   email: string;
 *   user: { name: string; }
 * }>;
 *
 * export const suite = staticSuite(
 *   (data: FormModel, field?: FormFieldName<FormModel>) => {
 *     only(field);
 *
 *     /// ✅ Autocomplete works
 *     test('email', 'Required', () => {
 *       enforce(data.email).isNotBlank();
 *     });
 *
 *     test('user.name', 'Required', () => {
 *       enforce(data.user?.name).isNotBlank();
 *     });
 *
 *     /// Form-level validation
 *     test(ROOT_FORM, 'At least one contact method', () => {
 *       enforce(data.email || data.user?.name).isTruthy();
 *     });
 *   }
 * );
 * ```
 */
type FormFieldName<T> = FieldPath<T> | typeof ROOT_FORM;

/**
 * Helper type to infer the value type at a given field path.
 * Useful for creating type-safe utilities that work with field paths.
 *
 * @template T - The model type
 * @template Path - The field path string
 *
 * @example
 * ```typescript
 * type Model = {
 *   user: {
 *     profile: {
 *       age: number;
 *     }
 *   }
 * };
 *
 * type AgeType = FieldPathValue<Model, 'user.profile.age'>;
 * /// Result: number
 * ```
 */
type FieldPathValue<T, Path extends string> = Path extends keyof T ? T[Path] : Path extends `${infer K}.${infer Rest}` ? K extends keyof T ? FieldPathValue<NonNullable<T[K]>, Rest> : never : never;
/**
 * Utility type to check if a path is valid for a given model.
 * Returns the path if valid, never otherwise.
 *
 * @template T - The model type
 * @template Path - The path to validate
 *
 * @example
 * ```typescript
 * type Model = { name: string; age: number; };
 *
 * type Valid = ValidateFieldPath<Model, 'name'>; // 'name'
 * type Invalid = ValidateFieldPath<Model, 'invalid'>; // never
 * ```
 */
type ValidateFieldPath<T, Path extends string> = Path extends FieldPath<T> ? Path : never;
/**
 * Extract all leaf field paths (paths that point to primitive values).
 * Useful when you only want paths to actual values, not intermediate objects.
 *
 * @template T - The model type
 *
 * @example
 * ```typescript
 * type Model = {
 *   user: {
 *     name: string;
 *     profile: {
 *       age: number;
 *     }
 *   }
 * };
 *
 * type Leaves = LeafFieldPath<Model>;
 * /// Result: 'user.name' | 'user.profile.age'
 * /// Note: 'user' and 'user.profile' are excluded (not leaves)
 * ```
 */
type LeafFieldPath<T, Prefix extends string = '', Depth extends readonly number[] = []> = Depth['length'] extends 10 ? never : T extends Primitive ? never : T extends ReadonlyArray<infer U> ? LeafFieldPath<U, Prefix, [...Depth, 1]> : {
    [K in keyof T & string]: T[K] extends Primitive ? `${Prefix}${K}` : T[K] extends ReadonlyArray<infer U> ? U extends Primitive ? never : LeafFieldPath<U, `${Prefix}${K}.`, [...Depth, 1]> : LeafFieldPath<T[K], `${Prefix}${K}.`, [...Depth, 1]>;
}[keyof T & string];

type NgxFirstInvalidOptions = {
    /** Scroll animation behavior (default: `'smooth'`, or `'auto'` when reduced motion is preferred). */
    behavior?: ScrollBehavior;
    /** Vertical alignment when scrolling (default: `'center'`). */
    block?: ScrollLogicalPosition;
    /** Horizontal alignment when scrolling (default: `'nearest'`). */
    inline?: ScrollLogicalPosition;
    /** Whether to focus after scrolling (default: `true`). */
    focus?: boolean;
    /** Passed to `focus()` when focusing (default: `true`). */
    preventScrollOnFocus?: boolean;
    /** Opens ancestor `<details>` elements before scroll/focus (default: `true`). */
    openCollapsedParents?: boolean;
    /** Selector used to locate the first invalid element. */
    invalidSelector?: string;
    /** Selector used to resolve the best focus target within the invalid element. */
    focusSelector?: string;
};
declare const DEFAULT_INVALID_SELECTOR: string;
declare const DEFAULT_FOCUS_SELECTOR: string;

/**
 * Represents the state of a form managed by scVestForm directive.
 * This is the structure returned by NgxVestFormDirective.formState() or similar.
 */
type NgxFormState<TModel = unknown> = {
    /** Whether the form is valid */
    valid: boolean;
    /** Map of field errors by field path */
    errors: Record<string, string[]>;
    /** Current form value (includes disabled fields) */
    value: TModel | null;
};
/**
 * Creates an empty NgxFormState with default values.
 *
 * This utility is helpful when you need to provide a fallback for components
 * that require a non-null NgxFormState, such as when child components
 * might not be initialized yet.
 *
 * @template TModel The type of the form model/value
 * @returns A complete NgxFormState object with empty/default values
 *
 * @example
 * ```typescript
 * /// In a parent component that displays child form state
 * protected readonly formState = computed(() =>
 *   this.childForm()?.formState() ?? createEmptyFormState()
 * );
 * ```
 *
 * @example
 * ```typescript
 * /// With specific typing
 * interface MyFormModel {
 *   name: string;
 *   email: string;
 * }
 *
 * const emptyState = createEmptyFormState<MyFormModel>();
 * ```
 */
declare function createEmptyFormState<TModel = unknown>(): NgxFormState<TModel>;

/**
 * FieldKey<T> gives you autocompletion for known string keys of T,
 * but also allows any string (for dynamic/nested/cyclic field names).
 *
 * **Why use this?**
 * - Provides IDE autocomplete for known model properties
 * - Still accepts arbitrary strings for nested paths like `'addresses[0].street'`
 * - Useful for cyclic dependencies and dynamic field names
 *
 * **How it works:**
 * - Extracts string keys from T: `Extract<keyof T, string>`
 * - Unions with open-ended string: `string & {}` (branding trick)
 * - Result: autocomplete hints + accepts any string
 *
 * @template T The model type whose string keys should be suggested
 *
 * @example
 * ```typescript
 * interface User {
 *   name: string;
 *   email: string;
 *   age: number;
 * }
 *
 * /// Type: 'name' | 'email' | 'age' | string
 * type UserField = NgxFieldKey<User>;
 *
 * /// IDE suggests 'name', 'email', 'age'
 * /// but also accepts 'addresses[0].street'
 * const field: UserField = 'email'; // ✅ autocomplete
 * const nested: UserField = 'profile.settings.theme'; // ✅ also valid
 * ```
 *
 * Note: Use only string keys to avoid widening to number/symbol when T=any.
 */
type NgxFieldKey<T> = Extract<keyof T, string> | (string & {});
/**
 * Represents a Vest validation suite for use with ngx-vest-forms.
 *
 * @description
 * This type wraps Vest's `StaticSuite` with the specific generic parameters
 * required for form validation in this library.
 *
 * **Why use this type?**
 * - Simplifies the API by hiding complex generic parameters
 * - Ensures type safety for model and field parameters
 * - Accepts both string and FormFieldName<T> field parameters (from NgxTypedVestSuite)
 * - Provides better template compatibility (no `$any()` casts needed)
 * - Makes suite signatures consistent across the codebase
 *
 * **What it wraps:**
 * ```typescript
 * StaticSuite<string, string, (model: T, field?: string) => void>
 * ```
 *
 * **Type parameters explained:**
 * - First `string`: Field names (property paths like 'email' or 'addresses[0].street')
 * - Second `string`: Group names (for organizing tests, e.g., 'step1', 'step2')
 * - Third parameter: The validation function signature with bivariance trick
 *
 * **Field parameter:**
 * The field parameter accepts `string | undefined` for:
 * - Plain string field names like 'email'
 * - Nested paths like 'addresses.billing.street'
 * - undefined to run all tests
 *
 * **Bivariance for template compatibility:**
 * The callback type uses a bivariant method parameter trick to make
 * `NgxVestSuite<SpecificModel>` assignable to `NgxVestSuite<unknown>` in
 * Angular templates without requiring `$any()` casts. This preserves template
 * ergonomics while keeping implementation sites strictly typed.
 *
 * **Safety:**
 * The bivariant callback is only used for the suite's parameter type. The return
 * type remains fully typed. The model parameter remains strictly typed.
 *
 * @template T The model type that the validation suite operates on (default: unknown)
 *
 * @example
 * ```typescript
 * import { NgxDeepPartial, NgxVestSuite } from 'ngx-vest-forms';
 * import { staticSuite, test, enforce, only } from 'vest';
 *
 * type UserModel = NgxDeepPartial<{
 *   name: string;
 *   email: string;
 *   age: number;
 * }>;
 *
 * /// Create validation suite
 * export const userValidation: NgxVestSuite<UserModel> = staticSuite((model, field?) => {
 *   only(field); // Always call unconditionally
 *
 *   test('name', 'Name is required', () => {
 *     enforce(model.name).isNotBlank();
 *   });
 *
 *   test('email', 'Valid email is required', () => {
 *     enforce(model.email).isNotBlank().isEmail();
 *   });
 *
 *   test('age', 'Must be 18 or older', () => {
 *     enforce(model.age).greaterThanOrEquals(18);
 *   });
 * });
 *
 * /// Use in component
 * @Component({...})
 * class UserFormComponent {
 *   protected readonly suite = userValidation;
 *   protected readonly formValue = signal<UserModel>({});
 * }
 * ```
 *
 * @example
 * ```typescript
 * /// For dynamic/untyped scenarios, use unknown
 * const dynamicSuite: NgxVestSuite = create((model, field) => {
 *   only(field);
 *   /// Validation logic for any model structure
 * });
 * ```
 *
 * **Type design notes:**
 * - Default generic is `unknown` (safer than `any`) for opt-in typing
 * - Uses bivariant method parameter trick for better template assignability
 * - Field parameter accepts any string for nested/dynamic paths
 * - Return type is fully typed (void) to catch errors at call sites
 *
 * @see {@link https://vestjs.dev/docs/writing_your_suite | Vest Documentation}
 * @see {@link NgxFieldKey} For typed field name hints with arbitrary string support
 */
/** @internal Do not use outside ngx-vest-forms. The `any` is architecturally required. */
type NgxSuiteCallback<T> = {
    bivarianceHack(model: T, field?: any): void;
}['bivarianceHack'];
type NgxVestSuite<T = unknown> = StaticSuite<string, string, NgxSuiteCallback<T>>;
/**
 * Type-safe validation suite with autocomplete for field paths.
 * Use this when defining validation suites to get IDE autocomplete for field names.
 *
 * **Recommended Pattern:**
 * Always define validation suites with `NgxTypedVestSuite<T>` and let TypeScript infer the type in components:
 *
 * ```typescript
 * import { NgxTypedVestSuite, FormFieldName } from 'ngx-vest-forms';
 *
 * /// ✅ RECOMMENDED: Define with NgxTypedVestSuite for autocomplete
 * export const userSuite: NgxTypedVestSuite<UserModel> = staticSuite(
 *   (model: UserModel, field?: FormFieldName<UserModel>) => {
 *     only(field);
 *     /// ✅ IDE autocomplete for: 'email' | 'password' | 'profile.age' | typeof ROOT_FORM
 *     test('email', 'Required', () => enforce(model.email).isNotBlank());
 *   }
 * );
 *
 * /// ✅ In component: Use type inference (no explicit type)
 * @Component({...})
 * class MyFormComponent {
 *   protected readonly suite = userSuite; // ✅ Type inferred automatically
 *   protected readonly formValue = signal<UserModel>({});
 * }
 * ```
 *
 * **Why this pattern?**
 * - **Strong typing at definition**: `FormFieldName<T>` gives autocomplete for all field paths
 * - **Type inference in components**: No need for explicit types, avoids compatibility issues
 * - **Best of both worlds**: Type safety where you write validation logic, convenience in components
 *
 * @template T The model type that the validation suite operates on
 *
 * @see {@link NgxVestSuite} For the base suite type (accepts any string field)
 * @see {@link FormFieldName} For the field name type with autocomplete
 */
/** @internal Do not use outside ngx-vest-forms. */
type NgxTypedSuiteCallback<T> = {
    bivarianceHack(model: T, field?: FormFieldName<T>): void;
}['bivarianceHack'];
type NgxTypedVestSuite<T> = StaticSuite<string, string, NgxTypedSuiteCallback<T>>;

/**
 * Type for validation configuration that accepts both the typed and untyped versions.
 * This ensures backward compatibility while supporting the new typed API.
 */
type NgxValidationConfig<T = unknown> = Record<string, string[]> | ValidationConfigMap<T> | null;
/**
 * Payload emitted when a named control inside the form loses focus.
 *
 * This is intentionally low-level so app code can build workflows such as
 * draft auto-save, analytics, or blur-driven side effects without the form
 * library taking ownership of persistence behavior.
 *
 * It is not intended as a blur-time workaround for dependent field validation;
 * for that pattern, prefer `validationConfig` plus each target field's own
 * `errorDisplayMode`.
 *
 * The emitted `field` is the full dotted control path (e.g.
 * `passwords.confirm`, `businessHours.values.0.from`) regardless of whether
 * the surrounding groups use static `ngModelGroup="key"` or dynamic
 * `[ngModelGroup]="expr"` bindings, because the path is read from the live
 * `NgModel` directive registered with the form.
 *
 * @publicApi
 */
type NgxFieldBlurEvent<T = unknown> = {
    field: string;
    value: unknown;
    formValue: T | null;
    dirty: boolean;
    touched: boolean;
    valid: boolean;
    pending: boolean;
};
/**
 * Main form directive for ngx-vest-forms that bridges Angular template-driven forms with Vest.js validation.
 *
 * This directive provides:
 * - **Unidirectional data flow**: Use `[ngModel]` (not `[(ngModel)]`) with `(formValueChange)` for predictable state updates
 * - **Vest.js integration**: Automatic async validators from Vest suites with field-level optimization
 * - **Validation dependencies**: Configure cross-field validation triggers via `validationConfig`
 * - **Form state**: Access validity, errors, and values through the `formState` signal
 *
 * @usageNotes
 *
 * ### Basic Usage
 * ```html
 * <form ngxVestForm [suite]="validationSuite" (formValueChange)="formValue.set($event)">
 *   <input name="email" [ngModel]="formValue().email" />
 * </form>
 * ```
 *
 * ### With Validation Dependencies
 * ```html
 * <form ngxVestForm [suite]="suite" [validationConfig]="validationConfig">
 *   <input name="password" [ngModel]="formValue().password" />
 *   <input name="confirmPassword" [ngModel]="formValue().confirmPassword" />
 * </form>
 * ```
 * ```typescript
 * validationConfig = { 'password': ['confirmPassword'] };
 * ```
 *
 * ### Accessing Form State
 * ```typescript
 * vestForm = viewChild.required('vestForm', { read: FormDirective });
 * isValid = computed(() => this.vestForm().formState().valid);
 * ```
 *
 * @see {@link https://github.com/ngx-vest-forms/ngx-vest-forms} for full documentation
 * @publicApi
 */
declare class FormDirective<T extends Record<string, unknown>> {
    #private;
    readonly ngForm: NgForm;
    private readonly elementRef;
    private readonly destroyRef;
    private readonly cdr;
    private readonly configDebounceTime;
    /**
     * Public signal storing field warnings keyed by field path.
     * This allows warnings to be stored and displayed without affecting field validity.
     * Angular's control.errors !== null marks a field as invalid, so we store warnings
     * separately when they exist without errors.
     */
    readonly fieldWarnings: _angular_core.WritableSignal<Map<string, readonly string[]>>;
    /**
     * Computed signal that returns field paths for all touched (or submitted) leaf controls.
     * Updates reactively when controls are touched (blur) or when form status changes.
     *
     * This enables consumers to determine which fields the user has interacted with,
     * useful for filtering errors/warnings to match the form's visible validation state.
     *
     * @publicApi
     */
    readonly touchedFieldPaths: _angular_core.Signal<string[]>;
    /**
     * Computed signal for form state with validity and errors.
     * Used by templates and tests as vestForm.formState().valid/errors
     *
     * Uses custom equality function to prevent unnecessary recalculations
     * when form status changes but actual values/errors remain the same.
     */
    readonly formState: _angular_core.Signal<NgxFormState<T>>;
    /**
     * The value of the form, this is needed for the validation part.
     * Using input() here because two-way binding is provided via formValueChange output.
     * In the minimal core directive (form-core.directive.ts), this would be model() instead.
     */
    readonly formValue: InputSignal<T | null>;
    /**
     * Static vest suite that will be used to feed our angular validators.
     * Accepts both NgxVestSuite and NgxTypedVestSuite through compatible type signatures.
     * NgxTypedVestSuite<T> is assignable to NgxVestSuite<T> due to bivariance and
     * FormFieldName<T> (string literal union) being assignable to string.
     */
    readonly suite: InputSignal<NgxVestSuite<T> | NgxTypedVestSuite<T> | null>;
    /**
     * The shape of our form model. This is a deep required version of the form model
     * The goal is to add default values to the shape so when the template-driven form
     * contains values that shouldn't be there (typo's) that the developer gets run-time
     * errors in dev mode
     */
    readonly formShape: InputSignal<ngx_vest_forms.NgxDeepRequired<T> | null>;
    /**
     * Updates the validation config which is a dynamic object that will be used to
     * trigger validations on the dependant fields
     * Eg: ```typescript
     * validationConfig = {
     *     'passwords.password': ['passwords.confirmPassword']
     * }
     * ```
     *
     * This will trigger the updateValueAndValidity on passwords.confirmPassword every time the passwords.password gets a new value
     *
     * @param v
     */
    readonly validationConfig: InputSignal<NgxValidationConfig<T>>;
    /**
     * Emits whenever validation feedback may have changed, even if the aggregate
     * root form status string stays the same.
     */
    private readonly validationFeedback$;
    private readonly pending$;
    /**
     * Emits every time the form status changes in a state
     * that is not PENDING
     * We need this to assure that the form is in 'idle' state
     */
    readonly idle$: Observable<"VALID" | "INVALID" | "DISABLED">;
    /**
     * Triggered as soon as the form value changes
     * It also contains the disabled values (raw values)
     *
     * Cleanup is handled automatically by the directive when it's destroyed.
     */
    readonly formValueChange: _angular_core.OutputRef<T>;
    /**
     * Emits an object with all the errors of the form
     * every time a form control or form groups changes its status to valid or invalid
     *
     * For submit events, waits for async validation (including ROOT_FORM) to complete
     * before emitting errors. This ensures ROOT_FORM errors are included in the output.
     *
     * Cleanup is handled automatically by the directive when it's destroyed.
     */
    readonly errorsChange: _angular_core.OutputRef<Record<string, string[]>>;
    /**
     * Triggered as soon as the form becomes dirty
     *
     * Cleanup is handled automatically by the directive when it's destroyed.
     */
    readonly dirtyChange: _angular_core.OutputRef<boolean>;
    /**
     * Fired when the status of the root form changes.
     */
    private readonly statusChanges$;
    /**
     * Triggered When the form becomes valid but waits until the form is idle
     *
     * Cleanup is handled automatically by the directive when it's destroyed.
     */
    readonly validChange: _angular_core.OutputRef<boolean>;
    /**
     * Emits when a named control inside the form loses focus.
     *
     * Useful for application-level workflows such as draft auto-save on blur.
     */
    readonly fieldBlur: _angular_core.OutputEmitterRef<NgxFieldBlurEvent<T>>;
    /**
     * Track validation in progress to prevent circular triggering (Issue #19)
     */
    private readonly validationInProgress;
    constructor();
    /**
     * Manually trigger form validation update.
     *
     * This is useful when form structure changes but no control values change,
     * which means validation state might be stale. This method forces a re-evaluation
     * of all form validators and updates the form validity state.
     *
     * **IMPORTANT: This method validates ALL form fields by design.**
     * This is intentional for structure changes as conditional validators may now
     * apply to different fields, requiring a complete validation refresh.
     *
     * **CRITICAL: This method does NOT mark fields as touched or show errors.**
     * It only re-runs validation logic. To show all errors (e.g., on submit),
     * use `markAllAsTouched()` instead or in combination.
     *
     * **When to use each:**
     * - `triggerFormValidation()` - Re-run validation when structure changes
     * - `markAllAsTouched()` - Show all errors to user (e.g., on submit)
     * - Both together - Rare, only if structure changed AND you want to show errors
     *
     * **Note on form submission:**
     * When using the default error display mode (`on-blur-or-submit`), you typically
     * don't need to call this method on submit. The form directive automatically marks
     * all fields as touched on `ngSubmit`, and errors will display automatically.
     * Only use this method when form structure changes without value changes.
     *
     * **Use Cases:**
     * - Conditionally showing/hiding form controls based on other field values
     * - Adding or removing form controls dynamically
     * - Switching between different form layouts where validation requirements change
     * - Any scenario where form structure changes but no ValueChangeEvent is triggered
     *
     * **Example:**
     * When switching from a form with required input fields to one with only informational content,
     * the form should become valid, but this won't happen automatically
     * when no value changes occur (e.g., switching from input fields to informational content).
     *
     * **Performance Note:**
     * This method calls `updateValueAndValidity({ emitEvent: true })` on the root form,
     * which validates all form controls. For large forms, consider if more granular
     * validation updates are possible.
     *
     * @example
     * ```typescript
     * /// After changing form structure
     * onProcedureTypeChange(newType: string) {
     *   this.procedureType.set(newType);
     *   /// Structure changed but no control values changed
     *   this.formDirective.triggerFormValidation();
     * }
     *
     * /// For submit with multiple forms (show all errors)
     * submitAll() {
     *   // Mark all as touched to show errors
     *   this.form1Ref().markAllAsTouched();
     *   this.form2Ref().markAllAsTouched();
     *   // Only needed if structure changed without value changes
     *   // this.form1Ref().triggerFormValidation();
     *   // this.form2Ref().triggerFormValidation();
     * }
     * ```
     */
    triggerFormValidation(path?: string): void;
    /**
     * Convenience method to mark all form controls as touched.
     *
     * This is useful for showing all validation errors at once, typically when
     * the user clicks a submit button. When a field is marked as touched,
     * the error display logic (based on `errorDisplayMode`) will show its errors.
     *
     * **Note on automatic behavior:**
     * When using the default error display mode (`on-blur-or-submit`), you typically
     * don't need to call this method manually for regular form submissions. The form
     * directive automatically marks all fields as touched on `ngSubmit`, so errors
     * will display automatically when the user submits the form.
     *
     * **When to use this method:**
     * - Multiple forms with a single submit button (forms without their own submit)
     * - Programmatic form submission without triggering `ngSubmit`
     * - Custom validation flows outside the normal submit process
     *
     * **Note:** This method only marks fields as touched—it does NOT re-run validation.
     * If you also need to re-run validation (e.g., after structure changes), call
     * `triggerFormValidation()` as well.
     *
     * @example
     * ```typescript
     * /// Standard form submission - NO need to call markAllAsTouched()
     * /// The directive handles this automatically on ngSubmit
     * <form ngxVestForm (ngSubmit)="save()">
     *   <button type="submit">Submit</button>
     * </form>
     *
     * /// Multiple forms with one submit button
     * submitAll() {
     *   this.form1().markAllAsTouched();
     *   this.form2().markAllAsTouched();
     *   if (this.form1().formState().valid && this.form2().formState().valid) {
     *     /// Submit all forms
     *   }
     * }
     * ```
     */
    markAllAsTouched(): void;
    /**
     * Clears the current submit cycle without resetting control values or metadata.
     *
     * Unlike {@link resetForm}, this only flips the submitted gate back to `false`.
     * Touched/dirty/pristine state is preserved so consumers can end `'on-submit'`
     * error visibility without a full form reset.
     *
     * **When to use:**
     * - You use submit-gated error visibility such as `'on-submit'`
     * - A submit attempt already happened
     * - The user resolved the current submit-time errors
     * - You want future untouched fields to wait for the next submit before showing errors
     *
     * **Why this exists:**
     * `resetForm()` would also clear touched/dirty/pristine metadata, which is often
     * too disruptive for long-form, multi-form, or mixed error-display flows.
     *
     * **What it does NOT do:**
     * - Does not change field values
     * - Does not mark controls pristine or untouched
     * - Does not re-run validation
     *
     * @example
     * ```typescript
     * submitAll(): void {
     *   for (const form of this.submitForms()) {
     *     form.ngForm.onSubmit(new Event('submit'));
     *   }
     *
     *   if (this.submitForms().every((form) => form.formState().valid)) {
     *     for (const form of this.submitForms()) {
     *       form.clearSubmittedState();
     *     }
     *   }
     * }
     * ```
     *
     * @see {@link resetForm} to fully reset values and control metadata
     * @see {@link markAllAsTouched} to manually show all errors
     * @see {@link triggerFormValidation} to re-run validation after structure changes
     */
    clearSubmittedState(): void;
    /**
     * Finds the first invalid element in this form, scrolls it into view, and focuses it.
     *
     * Useful in custom submit flows where `markAllAsTouched()` is triggered externally
     * and the app then wants to guide keyboard and assistive-technology users to the
     * first failing field.
     *
     * @returns The focused element when a focusable target exists, otherwise the first
     *          matched invalid element. Returns `null` when no invalid element is found.
     */
    focusFirstInvalidControl(options?: NgxFirstInvalidOptions): HTMLElement | null;
    /**
     * Finds and scrolls the first invalid element into view without moving focus.
     *
     * @returns The resolved element, or `null` when no invalid element is found.
     */
    scrollToFirstInvalidControl(options?: NgxFirstInvalidOptions): HTMLElement | null;
    /**
     * Host handler: called whenever any descendant field loses focus.
     * Used to make touched-path tracking react immediately on blur/tab.
     */
    onFormFocusOut(event: FocusEvent): void;
    /**
     * Resets the form to a pristine, untouched state with optional new values.
     *
     * This method properly resets the form by:
     * 1. Resetting Angular's underlying NgForm with the provided value
     * 2. Clearing the bidirectional sync tracking state
     * 3. Forcing a form validity update to clear any stale validation errors
     *
     * **Why this method exists:**
     * When using the pattern `formValue.set({})` to reset a form, there can be a timing
     * issue where the form controls in the DOM still hold their old values while the
     * signal has already been updated. This creates a conflict in the bidirectional
     * sync logic, requiring workarounds like calling `formValue.set({})` twice with
     * a setTimeout. This method provides a proper solution by:
     * - Calling Angular's `NgForm.resetForm()` which properly clears all controls
     * - Clearing the internal sync tracking state to avoid stale comparisons
     * - Triggering a form validity update to ensure validation state is current
     *
     * **Usage:**
     * Instead of the double-set workaround:
     * ```typescript
     * // ❌ Old workaround (avoid)
     * reset(): void {
     *   this.formValue.set({});
     *   setTimeout(() => this.formValue.set({}), 0);
     * }
     *
     * // ✅ Preferred approach
     * vestForm = viewChild.required('vestForm', { read: FormDirective });
     * reset(): void {
     *   this.formValue.set({});
     *   this.vestForm().resetForm();
     * }
     * ```
     *
     * **With new values:**
     * ```typescript
     * // Reset and set new initial values
     * resetWithDefaults(): void {
     *   const defaults = { firstName: '', lastName: '', age: 18 };
     *   this.formValue.set(defaults);
     *   this.vestForm().resetForm(defaults);
     * }
     * ```
     *
     * @param value - Optional new value to reset the form to. If not provided,
     *                resets to empty/default values.
     *
     * @see {@link markAllAsTouched} for showing validation errors
     * @see {@link triggerFormValidation} for re-running validation without reset
     */
    resetForm(value?: T | null): void;
    /**
     * Creates a one-shot async validator function for a specific field path.
     *
     * The returned validator:
     * - snapshots the current form model,
     * - injects the candidate control value at `field`,
     * - runs the Vest suite with debouncing,
     * - maps Vest errors/warnings into Angular `ValidationErrors | null`.
     *
     * Warnings are stored in `fieldWarnings` to keep warnings non-blocking when no errors exist.
     */
    createAsyncValidator(field: string, validationOptions: ValidationOptions): AsyncValidatorFn;
    static ɵfac: _angular_core.ɵɵFactoryDeclaration<FormDirective<any>, never>;
    static ɵdir: _angular_core.ɵɵDirectiveDeclaration<FormDirective<any>, "form[scVestForm], form[ngxVestForm]", ["scVestForm", "ngxVestForm"], { "formValue": { "alias": "formValue"; "required": false; "isSignal": true; }; "suite": { "alias": "suite"; "required": false; "isSignal": true; }; "formShape": { "alias": "formShape"; "required": false; "isSignal": true; }; "validationConfig": { "alias": "validationConfig"; "required": false; "isSignal": true; }; }, { "formValueChange": "formValueChange"; "errorsChange": "errorsChange"; "dirtyChange": "dirtyChange"; "validChange": "validChange"; "fieldBlur": "fieldBlur"; }, never, never, true, never>;
}

/**
 * Validates the root form (cross-field validation) using a Vest suite.
 *
 * Use this directive for form-wide validations that span multiple fields, such as:
 * - Password confirmation (`password` must match `confirmPassword`)
 * - Date range validation (`startDate` must be before `endDate`)
 * - Business rules like "at least one contact method required"
 *
 * @usageNotes
 *
 * ### Basic Usage
 * ```html
 * <form ngxVestForm ngxValidateRootForm [suite]="suite" (errorsChange)="errors.set($event)">
 *   <!-- form fields -->
 *   @if (errors()['rootForm']) {
 *     <div role="alert">{{ errors()['rootForm'][0] }}</div>
 *   }
 * </form>
 * ```
 *
 * ### Validation Modes
 * - `'submit'` (default): Validates only after form submission. Better UX for complex cross-field rules.
 * - `'live'`: Validates on every value change. Use sparingly for simple two-field comparisons.
 *
 * ```html
 * <form ngxVestForm ngxValidateRootForm [ngxValidateRootFormMode]="'live'">
 * ```
 *
 * ### Vest Suite Pattern
 * ```typescript
 * import { ROOT_FORM } from 'ngx-vest-forms';
 *
 * test(ROOT_FORM, 'Passwords must match', () => {
 *   enforce(model.confirmPassword).equals(model.password);
 * });
 * ```
 *
 * @see {@link https://github.com/ngx-vest-forms/ngx-vest-forms/blob/master/docs/VALIDATION-CONFIG-VS-ROOT-FORM.md}
 *
 * @example
 * ```html
 * <form scVestForm
 *       validateRootForm
 *       [suite]="suite"
 *       [formValue]="formValue()"
 *       [validateRootFormMode]="'submit'"
 *       (errorsChange)="errors.set($event)"
 *       #form="ngForm">
 *   <!-- form fields -->
 *   @if (errors()['rootForm']) {
 *     <div role="alert">{{ errors()['rootForm'][0] }}</div>
 *   }
 * </form>
 * ```
 *
 * @example
 * Validation suite:
 * ```typescript
 * import { ROOT_FORM } from 'ngx-vest-forms';
 *
 * export const suite = staticSuite((model, field?) => {
 *   only(field);
 *
 *   test(ROOT_FORM, 'Passwords must match', () => {
 *     enforce(model.confirmPassword).equals(model.password);
 *   });
 * });
 * ```
 *
 * @publicApi
 */
declare class ValidateRootFormDirective<T> implements AsyncValidator, AfterViewInit {
    private readonly injector;
    private readonly destroyRef;
    private readonly lastControl;
    validationOptions: _angular_core.InputSignal<ValidationOptions>;
    private readonly hasSubmitted;
    private readonly hasSubmitted$;
    private readonly formValue$;
    readonly formValue: _angular_core.InputSignal<T | null>;
    readonly suite: _angular_core.InputSignal<NgxVestSuite<T> | NgxTypedVestSuite<T> | null>;
    /**
     * Whether the root form should be validated or not
     * This will use the field rootForm
     * Accepts both validateRootForm and ngxValidateRootForm
     */
    readonly validateRootForm: _angular_core.InputSignalWithTransform<boolean, unknown>;
    readonly ngxValidateRootForm: _angular_core.InputSignalWithTransform<boolean, unknown>;
    /**
     * Validation mode:
     * - `'submit'` (effective default): Only validates after form submission.
     * - `'live'`: Validates on every value change.
     *
     * Both inputs default to `undefined` so we can detect whether the consumer
     * set them explicitly. Precedence is `ngx ?? legacy ?? 'submit'`, which
     * matches the documented behavior — observable only when both attributes
     * are set explicitly on the same form.
     */
    readonly validateRootFormMode: _angular_core.InputSignal<"submit" | "live" | undefined>;
    readonly ngxValidateRootFormMode: _angular_core.InputSignal<"submit" | "live" | undefined>;
    constructor();
    /**
     * Subscribe to form submit event using NgForm.ngSubmit EventEmitter
     * This approach avoids conflicts with component's (ngSubmit) handlers
     * Uses Injector to lazily get NgForm, avoiding circular dependency
     * (Directive → NgForm → AsyncValidators → Directive)
     */
    ngAfterViewInit(): void;
    validate(control: AbstractControl): Observable<ValidationErrors | null>;
    createAsyncValidator(field: typeof ROOT_FORM, validationOptions: ValidationOptions): AsyncValidatorFn;
    static ɵfac: _angular_core.ɵɵFactoryDeclaration<ValidateRootFormDirective<any>, never>;
    static ɵdir: _angular_core.ɵɵDirectiveDeclaration<ValidateRootFormDirective<any>, "form[validateRootForm], form[ngxValidateRootForm]", never, { "validationOptions": { "alias": "validationOptions"; "required": false; "isSignal": true; }; "formValue": { "alias": "formValue"; "required": false; "isSignal": true; }; "suite": { "alias": "suite"; "required": false; "isSignal": true; }; "validateRootForm": { "alias": "validateRootForm"; "required": false; "isSignal": true; }; "ngxValidateRootForm": { "alias": "ngxValidateRootForm"; "required": false; "isSignal": true; }; "validateRootFormMode": { "alias": "validateRootFormMode"; "required": false; "isSignal": true; }; "ngxValidateRootFormMode": { "alias": "ngxValidateRootFormMode"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
}

/**
 * The providers we need in every child component that holds an ngModelGroup
 */
declare const vestFormsViewProviders: (_angular_core.FactoryProvider | {
    provide: typeof ControlContainer;
    useExisting: typeof NgForm;
})[];
/**
 * Collection of directives, components, and modules required for ngx-vest-forms.
 *
 * This array exports all the necessary components and directives to enable
 * Vest validation integration with Angular template-driven forms.
 *
 * @example
 * ```typescript
 * import { NgxVestForms } from 'ngx-vest-forms';
 *
 * @Component({
 *
 *   imports: [NgxVestForms],
 *   /// ...
 * })
 * export class MyComponent { }
 * ```
 */
declare const NgxVestForms: readonly [typeof ValidateRootFormDirective, typeof ControlWrapperComponent, typeof FormGroupWrapperComponent, typeof FormControlStateDirective, typeof FormErrorDisplayDirective, typeof FormErrorControlDirective, typeof FormDirective, typeof FormsModule, typeof FormModelDirective, typeof FormModelGroupDirective];
/**
 * @deprecated Use `NgxVestForms` instead
 */
declare const vestForms: readonly [typeof ValidateRootFormDirective, typeof ControlWrapperComponent, typeof FormGroupWrapperComponent, typeof FormControlStateDirective, typeof FormErrorDisplayDirective, typeof FormErrorControlDirective, typeof FormDirective, typeof FormsModule, typeof FormModelDirective, typeof FormModelGroupDirective];

/**
 * Simple type that makes every property and child property
 * partial, recursively. Why? Because template-driven forms are
 * deep partial, since they get created by the DOM
 */
type DeepPartial<T> = {
    [P in keyof T]?: T[P] extends Array<infer U> ? Array<DeepPartial<U>> : T[P] extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>> : T[P] extends object ? DeepPartial<T[P]> : T[P];
};
/**
 * NgxDeepPartial - recommended alias for DeepPartial
 * Prevents naming conflicts with other libraries and clearly identifies ngx-vest-forms utilities.
 *
 * Makes every property and child property partial recursively.
 * Template-driven forms are inherently deep partial since they're created by the DOM.
 *
 * @example
 * ```typescript
 * type FormModel = NgxDeepPartial<{
 *   name: string;
 *   profile: { age: number; }
 * }>;
 * // Result: { name?: string; profile?: { age?: number; } }
 * ```
 */
type NgxDeepPartial<T> = DeepPartial<T>;

/**
 * Sometimes we want to make every property of a type
 * required, but also child properties recursively
 *
 * @template T The type to make deeply required
 * @example
 * ```typescript
 * interface User {
 *   name?: string;
 *   profile?: {
 *     age?: number;
 *   };
 * }
 *
 * type RequiredUser = NgxDeepRequired<User>;
 * /// Result: { name: string; profile: { age: number; } }
 * ```
 */
type NgxDeepRequired<T> = {
    [K in keyof T]-?: T[K] extends Date | Function ? T[K] : T[K] extends Array<infer U> ? Array<NgxDeepRequired<U>> : T[K] extends ReadonlyArray<infer U> ? ReadonlyArray<NgxDeepRequired<U>> : T[K] extends object ? NgxDeepRequired<T[K]> : T[K];
};
/**
 * A specialized version of NgxDeepRequired that handles form compatibility issues,
 * specifically the Date/string type mismatch that occurs in form initialization.
 *
 * **Problem this solves:**
 * - Model interfaces often use `Date` types for semantic correctness
 * - UI libraries (like PrimeNG p-calendar) require empty string `''` for placeholder display
 * - This creates a `Date !== string` type mismatch during form initialization
 *
 * **Solution:**
 * - Makes all properties required (removes optional `?` modifiers)
 * - Recursively processes nested objects
 * - **Only** adds `string` as an allowed type for `Date` properties
 * - All other types remain unchanged to maintain type safety
 *
 * **Usage Example:**
 * ```typescript
 * interface UserModel {
 *   id?: number;
 *   name?: string;
 *   birthDate?: Date;
 *   profile?: {
 *     createdAt?: Date;
 *     isActive?: boolean;
 *   };
 * }
 *
 * /// Result type:
 * type FormCompatibleUser = NgxFormCompatibleDeepRequired<UserModel>;
 * /// {
 * ///   id: number;
 * ///   name: string;
 * ///   birthDate: Date | string;  /// <-- Only Date gets union treatment
 * ///   profile: {
 * ///     createdAt: Date | string; /// <-- Date properties at all levels
 * ///     isActive: boolean;        /// <-- Other types unchanged
 * ///   };
 * /// }
 *
 * /// Usage in component:
 * const formShape: FormCompatibleUser = {
 *   id: 0,
 *   name: '',
 *   birthDate: '',  // ✅ Valid! Can use empty string for placeholder
 *   profile: {
 *     createdAt: '', // ✅ Valid! Empty string for date inputs
 *     isActive: false
 *   }
 * };
 * ```
 *
 * **Why not just make everything `T | string`?**
 * - That would sacrifice type safety for non-Date fields
 * - This approach only relaxes types where the form-compatibility issue exists
 * - Maintains strict typing for booleans, numbers, strings, etc.
 *
 * **When to use:**
 * - Creating form shapes for `formShape` validation
 * - Initializing form models that may have empty date inputs
 * - Working with date picker components that accept empty strings
 *
 * **When NOT to use:**
 * - For API response types (use the original model interface)
 * - For non-form data structures
 * - When you don't have Date fields that need empty string support
 *
 * @template T The type to make form-compatible with required properties
 * @see {@link NgxDeepRequired} For the base deep required functionality without form compatibility
 * @see {@link https://github.com/ngx-vest-forms/ngx-vest-forms/issues/12 | Issue #12}
 */
type NgxFormCompatibleDeepRequired<T> = {
    [K in keyof T]-?: T[K] extends Date | undefined ? Date | string : T[K] extends (...args: unknown[]) => unknown ? T[K] : T[K] extends Array<infer U> ? Array<NgxFormCompatibleDeepRequired<U>> : T[K] extends ReadonlyArray<infer U> ? ReadonlyArray<NgxFormCompatibleDeepRequired<U>> : T[K] extends object | undefined ? NgxFormCompatibleDeepRequired<NonNullable<T[K]>> : T[K];
};
/**
 * @deprecated
 * Deprecated since v1.2.0 (2024-06).
 * Use {@link NgxDeepRequired} instead for deep required types.
 * Migration: Replace `DeepRequired<T>` with `NgxDeepRequired<T>`.
 */
type DeepRequired<T> = NgxDeepRequired<T>;
/**
 * @deprecated
 * Deprecated since v1.2.0 (2024-06).
 * Use {@link NgxFormCompatibleDeepRequired} instead for form-compatible deep required types.
 * Migration: Replace `FormCompatibleDeepRequired<T>` with `NgxFormCompatibleDeepRequired<T>`.
 * Rationale: The new name is more consistent and descriptive.
 */
type FormCompatibleDeepRequired<T> = NgxFormCompatibleDeepRequired<T>;

/**
 * Fluent builder for creating type-safe validation configurations.
 *
 * Provides convenience methods for common validation dependency patterns:
 * - **`whenChanged()`**: One-way dependencies (when A changes, revalidate B)
 * - **`bidirectional()`**: Two-way dependencies (A ↔ B revalidate each other)
 * - **`group()`**: All fields in group revalidate each other
 * - **`merge()`**: Combine with existing configurations
 *
 * @template T - The form model type
 *
 * @example Basic usage
 * ```typescript
 * const config = createValidationConfig<MyFormModel>()
 *   .whenChanged('password', 'confirmPassword')
 *   .bidirectional('startDate', 'endDate')
 *   .build();
 * ```
 *
 * @example Complex scenario
 * ```typescript
 * const config = createValidationConfig<OrderFormModel>()
 *   .group(['firstName', 'lastName', 'email'])
 *   .whenChanged('country', ['state', 'zipCode'])
 *   .bidirectional('minPrice', 'maxPrice')
 *   .merge(existingConfig)
 *   .build();
 * ```
 */
declare class ValidationConfigBuilder<T> {
    private config;
    /**
     * Add a one-way dependency: when `trigger` changes, revalidate `dependents`.
     *
     * This method is cumulative - calling it multiple times with the same trigger
     * will merge all dependents together.
     *
     * **When to Use:**
     * - ✅ Conditional field requirements (country → state/zipCode)
     * - ✅ Calculated fields (quantity/price → total)
     * - ✅ Dependent validations where only one direction matters
     * - ✅ Cascading validations (A changes → B needs revalidation → C needs revalidation)
     *
     * **Real-World Use Case:**
     * In an e-commerce checkout form, when the user selects a country, the available
     * states/provinces and zip code format validation rules change. The country field
     * needs to trigger revalidation of state and zipCode, but changes to state/zipCode
     * don't need to revalidate country (one-way dependency).
     *
     * **Vest.js Patterns That Work Well:**
     * - `skipWhen(res => res.hasErrors('trigger'), () => { test('dependent', ...) })` -
     *   Skip expensive validations on dependent field until trigger field is valid
     * - Async validations on dependent fields that need the trigger's value
     * - Calculated field validations (e.g., total depends on quantity × price)
     * - Conditional `omitWhen` based on trigger field state
     *
     * @param trigger - The field that triggers revalidation
     * @param revalidate - Single field or array of fields to revalidate
     * @returns This builder instance for method chaining
     *
     * @example Single dependent
     * ```typescript
     * builder.whenChanged('password', 'confirmPassword');
     * /// Result: { password: ['confirmPassword'] }
     * ```
     *
     * @example Multiple dependents
     * ```typescript
     * builder.whenChanged('country', ['state', 'zipCode', 'postalCode']);
     * /// Result: { country: ['state', 'zipCode', 'postalCode'] }
     * ```
     *
     * @example Cumulative calls
     * ```typescript
     * builder
     *   .whenChanged('password', 'confirmPassword')
     *   .whenChanged('password', 'securityScore');
     * /// Result: { password: ['confirmPassword', 'securityScore'] }
     * ```
     */
    whenChanged<K extends FieldPath<T>>(trigger: K, revalidate: FieldPath<T> | ReadonlyArray<FieldPath<T>>): this;
    /**
     * Add bidirectional dependency: when either field changes, revalidate the other.
     *
     * This is a convenience method that calls `whenChanged()` in both directions.
     * Commonly used for password/confirmPassword, min/max ranges, start/end dates.
     *
     * **When to Use:**
     * - ✅ Password confirmation (password ↔ confirmPassword)
     * - ✅ Min/max range validation (minPrice ↔ maxPrice, minAge ↔ maxAge)
     * - ✅ Start/end date validation (startDate ↔ endDate)
     * - ✅ Any field comparison where both fields need mutual revalidation
     *
     * **Real-World Use Case:**
     * In a job posting form, when setting salary range, changing either minSalary or
     * maxSalary should revalidate the other to ensure minSalary ≤ maxSalary. Users
     * can change either field first, so both directions need validation. This prevents
     * invalid ranges like "$80k min, $50k max".
     *
     * **Vest.js Patterns That Work Well:**
     * - Comparison validations: `enforce(data.min).lessThanOrEquals(data.max)`
     * - Password matching: `enforce(data.password).equals(data.confirmPassword)`
     * - Date range validation with `enforce(data.startDate).lessThan(data.endDate)`
     * - `skipWhen` to prevent comparison when either field has errors:
     *   ```typescript
     *   skipWhen(res => res.hasErrors('field1') || res.hasErrors('field2'), () => {
     *     test('field1', 'Min must be ≤ Max', () => enforce(min).lessThanOrEquals(max));
     *   });
     *   ```
     *
     * @param field1 - First field in the bidirectional relationship
     * @param field2 - Second field in the bidirectional relationship
     * @returns This builder instance for method chaining
     *
     * @example Password confirmation
     * ```typescript
     * builder.bidirectional('password', 'confirmPassword');
     * /// Result: {
     * ///   password: ['confirmPassword'],
     * ///   confirmPassword: ['password']
     * /// }
     * ```
     *
     * @example Date range
     * ```typescript
     * builder.bidirectional('startDate', 'endDate');
     * /// Result: {
     * ///   startDate: ['endDate'],
     * ///   endDate: ['startDate']
     * /// }
     * ```
     */
    bidirectional<K1 extends FieldPath<T>, K2 extends FieldPath<T>>(field1: K1, field2: K2): this;
    /**
     * Create a validation group where all fields revalidate each other.
     *
     * When any field in the group changes, all other fields in the group are revalidated.
     * This is useful for fields that collectively form a validation rule.
     *
     * **When to Use:**
     * - ✅ "At least one required" scenarios (email OR phone OR address)
     * - ✅ Interdependent field sets (credit card: number + expiry + CVV)
     * - ✅ Contact information (firstName + lastName + email)
     * - ✅ Fields that collectively satisfy a business rule
     *
     * **Real-World Use Case:**
     * In a customer registration form with a "at least one contact method required"
     * business rule, the validation suite checks `email || phone || mailingAddress`.
     * When user fills any of these fields, the other fields need revalidation to clear
     * the "at least one required" error. All three fields are interdependent, so
     * grouping them ensures validation updates correctly regardless of which field
     * the user fills first.
     *
     * **Vest.js Patterns That Work Well:**
     * - "At least one required" logic:
     *   ```typescript
     *   test('email', 'Provide at least one contact method', () => {
     *     enforce(data.email || data.phone || data.address).isTruthy();
     *   });
     *   ```
     * - Interdependent field validation where all fields collectively satisfy a rule
     * - Credit card validation (number + expiry + CVV all need each other)
     * - Use `optional()` for groups where fields can all be empty together:
     *   ```typescript
     *   optional(['email', 'phone', 'address']);
     *   ```
     *
     * **Performance Note:** This creates N×(N-1) dependencies, which can impact
     * performance for large groups. Consider using `whenChanged()` for more targeted
     * dependencies if performance is a concern (e.g., groups > 10 fields).
     *
     * @param fields - Array of fields that should all revalidate each other
     * @returns This builder instance for method chaining
     *
     * @example Contact information group
     * ```typescript
     * builder.group(['firstName', 'lastName', 'email']);
     * /// Result: {
     * ///   firstName: ['lastName', 'email'],
     * ///   lastName: ['firstName', 'email'],
     * ///   email: ['firstName', 'lastName']
     * /// }
     * ```
     *
     * @example Address validation group
     * ```typescript
     * builder.group(['street', 'city', 'state', 'zipCode']);
     * ```
     */
    group<K extends FieldPath<T>>(fields: readonly K[]): this;
    /**
     * Merge with an existing validation configuration.
     *
     * This method is useful for:
     * - Combining base configurations with conditional configurations
     * - Composing configurations from multiple sources
     * - Adding dynamic configurations based on runtime conditions
     *
     * **When to Use:**
     * - ✅ Conditional features (international shipping adds customs fields)
     * - ✅ Reusable configuration modules (address config, payment config)
     * - ✅ Dynamic forms where validation rules change at runtime
     * - ✅ Feature flags or A/B testing variations
     *
     * **Real-World Use Case:**
     * In an e-commerce checkout, your form has base validation (shipping address,
     * payment info). When user selects international shipping, you need additional
     * validations (customs declaration, tax ID). Instead of duplicating the base
     * config, use `merge()` to conditionally add the international validation rules.
     * This keeps your code DRY and makes it easy to toggle features on/off.
     *
     * **Vest.js Patterns That Work Well:**
     * - Conditional features with `omitWhen`:
     *   ```typescript
     *   omitWhen(!isInternational, () => {
     *     test('customsForm', 'Customs declaration required', () => {
     *       enforce(data.customsForm).isNotBlank();
     *     });
     *   });
     *   ```
     * - Reusable validation modules that can be composed
     * - Feature flags: merge different configs based on enabled features
     * - A/B testing: conditionally merge test-specific validation rules
     * - Use with computed signals for reactive configuration:
     *   ```typescript
     *   const config = computed(() =>
     *     createValidationConfig<T>()
     *       .merge(baseConfig)
     *       .merge(featureEnabled() ? featureConfig : {})
     *       .build()
     *   );
     *   ```
     *
     * **Note:** Dependencies are merged and deduplicated. If both configs have
     * the same trigger field, dependents are combined into a single array.
     *
     * @param other - Existing ValidationConfigMap to merge
     * @returns This builder instance for method chaining
     *
     * @example Conditional configuration
     * ```typescript
     * const baseConfig = createValidationConfig<FormModel>()
     *   .bidirectional('password', 'confirmPassword')
     *   .build();
     *
     * const config = createValidationConfig<FormModel>()
     *   .merge(baseConfig)
     *   .merge(
     *     isInternational
     *       ? { country: ['customsForm'] }
     *       : {}
     *   )
     *   .build();
     * ```
     *
     * @example Composition
     * ```typescript
     * const addressConfig = { 'street': ['city', 'zipCode'] };
     * const contactConfig = { 'email': ['phone'] };
     *
     * const config = createValidationConfig<FormModel>()
     *   .merge(addressConfig)
     *   .merge(contactConfig)
     *   .build();
     * ```
     */
    merge(other: ValidationConfigMap<T>): this;
    /**
     * Build the final validation configuration object.
     *
     * Returns a deep copy of the configuration to prevent accidental mutations.
     * The returned object can be used with the `validationConfig` input of `scVestForm`.
     *
     * @returns Immutable ValidationConfigMap ready for use
     *
     * @example
     * ```typescript
     * protected readonly validationConfig = createValidationConfig<MyFormModel>()
     *   .bidirectional('password', 'confirmPassword')
     *   .whenChanged('country', 'state')
     *   .build();
     * ```
     */
    build(): ValidationConfigMap<T>;
}
/**
 * Factory function to create a type-safe validation configuration builder.
 *
 * This is the recommended entry point for creating validation configurations.
 * It provides full type safety and IDE autocomplete for field names.
 *
 * @template T - The form model type
 * @returns A new ValidationConfigBuilder instance
 *
 * @example Basic usage
 * ```typescript
 * protected readonly validationConfig = createValidationConfig<FormModel>()
 *   .bidirectional('password', 'confirmPassword')
 *   .whenChanged('country', 'state')
 *   .build();
 * ```
 *
 * @example With DeepPartial form model
 * ```typescript
 * type FormModel = DeepPartial<{
 *   user: {
 *     profile: {
 *       email: string;
 *       phone: string;
 *     }
 *   }
 * }>;
 *
 * const config = createValidationConfig<FormModel>()
 *   .bidirectional('user.profile.email', 'user.profile.phone')
 *   .build();
 * ```
 *
 * @example Complex scenario with all methods
 * ```typescript
 * const config = createValidationConfig<OrderFormModel>()
 *   .group(['firstName', 'lastName', 'email'])
 *   .bidirectional('startDate', 'endDate')
 *   .bidirectional('minPrice', 'maxPrice')
 *   .whenChanged('orderType', ['deliveryDate', 'priority'])
 *   .whenChanged('country', ['state', 'zipCode'])
 *   .merge(conditionalConfig)
 *   .build();
 * ```
 */
declare function createValidationConfig<T>(): ValidationConfigBuilder<T>;

/**
 * Converts a flat array to an object with numeric keys.
 * Does not recurse into nested arrays or objects.
 * Uses reduce() for optimal single-pass conversion.
 */
type DeepArrayToObject<T> = T extends ReadonlyArray<infer U> ? Record<number, DeepArrayToObject<U>> : T extends object ? {
    [K in keyof T]: DeepArrayToObject<T[K]>;
} : T;
declare function arrayToObject<T>(array: readonly T[]): Record<number, T>;
/**
 * Recursively converts arrays to objects with numeric keys, including nested arrays in objects.
 * Useful for template-driven forms that require object structure for nested arrays.
 */
declare function deepArrayToObject<T>(array: readonly T[]): Record<number, DeepArrayToObject<T>>;
/**
 * Converts selected numeric-keyed object properties back to arrays.
 * Only properties listed in keys will be converted; others remain untouched.
 * Useful for converting form models back to array format before saving/sending to backend.
 */
/**
 * Public API: Convert selected numeric-keyed object properties back to arrays.
 * Note: Conversion is explicit (by key) but will cascade inside converted branches
 * so nested numeric objects representing arrays become real arrays recursively.
 */
declare function objectToArray<const K extends readonly string[]>(object: unknown, keys: K): unknown;

/**
 * Utility functions for managing form field state in dynamic forms.
 * These functions help maintain component state consistency when form structure changes.
 */
/**
 * Conditionally clears fields from form state based on provided conditions.
 *
 * **CRITICAL: When This Utility is Required**
 * This utility is specifically needed when conditional logic switches between:
 * - **Form inputs** (e.g., `<input>`, `<select>`, `<textarea>`)
 * - **NON-form elements** (e.g., `<p>`, `<div>`, informational content)
 *
 * **Primary Use Case - Form Input ↔ Non-Form Content:**
 * ```typescript
 * @if (mode === 'input') {
 *   <input name="field" [ngModel]="value" />  // Form input
 * } @else {
 *   <p>No input required</p>                  // NON-form element
 * }
 * ```
 *
 * **Why This Creates a Problem:**
 * 1. Switching FROM input TO non-form content: Angular removes FormControl, but component signal retains old value
 * 2. This creates state inconsistency between `ngForm.form.value` (clean) and `formValue()` (stale)
 * 3. Manual clearing synchronizes component state with actual form structure
 *
 * **When NOT Required:**
 * Pure form-to-form conditionals usually don't need manual clearing:
 * ```typescript
 * @if (type === 'text') {
 *   <input name="field" [ngModel]="value" type="text" />
 * } @else {
 *   <input name="field" [ngModel]="value" type="number" />  // Still a form input
 * }
 * ```
 *
 * **Performance Note:**
 * Uses `Object.entries()` for efficient field clearing without mutation.
 * Only processes fields that need to be cleared, preserving other field values.
 *
 * @template T - The form model type, must extend Record<string, unknown>
 * @param currentState - The current form state object
 * @param conditions - Object mapping field names to boolean conditions (true = clear field)
 * @returns New state object with specified fields cleared (set to undefined)
 *
 * @example
 * ```typescript
 * ///  REQUIRED: Form input switching to non-form content
 * onProcedureTypeChange(newValue: string) {
 *   this.formValue.update((current) =>
 *     clearFieldsWhen(current, {
 *       fieldA: newValue !== 'typeA',  // Clear when switching to non-form content
 *       fieldB: newValue !== 'typeB',  // Clear when switching to non-form content
 *     })
 *   );
 * }
 *
 * ///  Template structure that REQUIRES clearing:
 * ///  @if (type === 'typeA') { <input name="fieldA" /> }
 * ///  @else if (type === 'typeB') { <input name="fieldB" /> }
 * ///  @else { <p>No input needed</p> }  // ← NON-form element!
 * ```
 *
 * @example
 * ```typescript
 * ///  Complex conditional clearing with mixed form/non-form scenarios
 * const cleanedState = clearFieldsWhen(form, {
 *   shippingAddress: !form.useShippingAddress,    // Clear when not using shipping
 *   emergencyContact: (form.age || 0) >= 18,      // Clear when adult (no emergency contact form)
 *   childInfo: form.userType !== 'parent',        // Clear when not parent (shows info text instead)
 * });
 * ```
 *
 * @example
 * ```typescript
 * ///  State inconsistency example - WHY manual clearing is needed:
 *
 * ///  BEFORE switching from input to non-form content (typeA → typeC):
 * formValue() = { procedureType: 'typeA', fieldA: 'user-input' }
 * ngForm.form.value = { procedureType: 'typeA', fieldA: 'user-input' }
 *
 * ///  AFTER switching WITHOUT clearing (PROBLEMATIC):
 * formValue() = { procedureType: 'typeC', fieldA: 'user-input' }  // ❌ Stale fieldA!
 * ngForm.form.value = { procedureType: 'typeC' }                  // ✅ Clean (Angular removed FormControl)
 *
 * ///  AFTER switching WITH clearing (CONSISTENT):
 * formValue() = { procedureType: 'typeC' }  // ✅ Clean component state
 * ngForm.form.value = { procedureType: 'typeC' }  // ✅ Clean form state
 * ```
 *
 */
declare function clearFieldsWhen<T extends Record<string, unknown>>(currentState: T, conditions: Readonly<Partial<Record<keyof T, boolean>>>): T;
/**
 * Clears multiple fields from form state unconditionally.
 *
 * **Use Cases:**
 * - Form reset operations when switching between form modes
 * - Clearing temporary/wizard data when exiting multi-step forms
 * - Cleanup after form submission or cancellation
 * - When you want explicit control over which fields to clear
 *
 * **Note:** Unlike `clearFieldsWhen`, this function always clears the specified fields
 * regardless of conditions. Use this when you want unconditional field removal.
 *
 * @template T - The form model type, must extend Record<string, unknown>
 * @param currentState - The current form state object
 * @param fieldsToClear - Array of field names to clear (set to undefined)
 * @returns New state object with specified fields cleared
 *
 * @example
 * ```typescript
 * ///  Clear specific fields during form mode transitions
 * const cleanedState = clearFields(currentFormValue, ['fieldA', 'fieldB']);
 *
 * ///  Reset wizard or temporary data
 * onFormModeChange() {
 *   this.formValue.update((current) =>
 *     clearFields(current, ['temporaryData', 'wizardStep', 'draftSaved'])
 *   );
 * }
 *
 * ///  Clear all optional fields on form submission
 * save() {
 *   const finalData = clearFields(this.formValue(), ['draftField', 'tempNotes']);
 *   this.submitForm(finalData);
 * }
 * ```
 */
declare function clearFields<T extends Record<string, unknown>>(currentState: T, fieldsToClear: ReadonlyArray<keyof T>): T;
/**
 * Creates a clean form state containing only fields that meet specified conditions.
 *
 * **Philosophy:** Instead of clearing unwanted fields (like `clearFieldsWhen`), this function
 * takes a "whitelist" approach - building a new state with only the fields you explicitly want to keep.
 *
 * **Best Use Cases:**
 * - Complex form transformations where you want to be explicit about kept fields
 * - Building clean data objects for API submission (exclude UI-only fields)
 * - Form mode switching where different modes have completely different field sets
 * - Data export scenarios where only certain fields should be included
 *
 * **Returns:** `Partial<T>` because the result may not contain all original fields.
 *
 * @template T - The form model type, must extend Record<string, unknown>
 * @param currentState - The current form state object
 * @param conditions - Object mapping field names to boolean conditions (true = keep field)
 * @returns New state object containing only fields where condition is true
 *
 * @example
 * ```typescript
 * ///  Keep only relevant fields based on form mode
 * const filteredState = keepFieldsWhen(currentFormValue, {
 *   procedureType: true, // always keep
 *   fieldA: procedureType === 'typeA',
 *   fieldB: procedureType === 'typeB',
 *   // Note: fieldC is omitted when procedureType === 'typeC' (non-form content)
 * });
 *
 * ///  Build clean data for API submission (exclude UI-only fields)
 * const apiData = keepFieldsWhen(formValue(), {
 *   // Business data - keep these
 *   userName: true,
 *   email: true,
 *   preferences: true,
 *   // UI state fields omitted: wizardStep, draftSaved, validationErrors, etc.
 * });
 *
 * ///  Dynamic form sections based on user permissions
 * const relevantData = keepFieldsWhen(form, {
 *   basicInfo: true,
 *   addressInfo: form.needsAddress && userCanEditAddress,
 *   paymentInfo: form.requiresPayment && userCanMakePayments,
 *   adminFields: userIsAdmin,
 * });
 * ```
 *
 * @since 1.0.0
 */
declare function keepFieldsWhen<T extends Record<string, unknown>>(currentState: T, conditions: Readonly<Partial<Record<keyof T, boolean>>>): Partial<T>;

/**
 * @internal
 * Internal utility for parsing field path strings.
 *
 * **Not intended for external use.** While this function can parse field paths,
 * it's primarily used internally. Most users won't need to parse field paths manually.
 * If you do need this functionality, consider using your own implementation tailored
 * to your specific needs.
 *
 * Converts a dot/bracket notation path (e.g. `'addresses[0].street'`)
 * to a Standard Schema path array (e.g. `['addresses', 0, 'street']`).
 *
 * **Use cases:**
 * - Converting Angular form paths to schema-compatible arrays
 * - Parsing Vest field names for array access
 * - Processing validation error paths from different sources
 *
 * **Supported formats:**
 * - Dot notation: `'user.name'` → `['user', 'name']`
 * - Bracket notation: `'addresses[0]'` → `['addresses', 0]`
 * - Mixed: `'user.addresses[0].street'` → `['user', 'addresses', 0, 'street']`
 *
 * @param path - The dot/bracket notation path string
 * @returns Array of path segments (strings for properties, numbers for array indices)
 *
 * @example
 * ```typescript
 * parseFieldPath('email')              // ['email']
 * parseFieldPath('user.profile.name')  // ['user', 'profile', 'name']
 * parseFieldPath('items[0]')           // ['items', 0]
 * parseFieldPath('users[0].addresses[1].street')
 * // ['users', 0, 'addresses', 1, 'street']
 * ```
 */
declare function parseFieldPath(path: string): Array<string | number>;
/**
 * Converts a Standard Schema path array (e.g. `['addresses', 0, 'street']`)
 * to a dot/bracket notation string (e.g. `'addresses[0].street'`).
 *
 * **Use cases:**
 * - Converting schema validation paths to Angular form field names
 * - Generating Vest field names from path arrays
 * - Creating human-readable field identifiers
 *
 * **Format rules:**
 * - String segments joined with dots: `['user', 'name']` → `'user.name'`
 * - Number segments use brackets: `['items', 0]` → `'items[0]'`
 * - Mixed paths combine both: `['users', 0, 'email']` → `'users[0].email'`
 *
 * @param path - Array of path segments (strings for properties, numbers for indices)
 * @returns Dot/bracket notation path string
 *
 * @example
 * ```typescript
 * stringifyFieldPath(['email'])                     // 'email'
 * stringifyFieldPath(['user', 'profile', 'name'])   // 'user.profile.name'
 * stringifyFieldPath(['items', 0])                  // 'items[0]'
 * stringifyFieldPath(['users', 0, 'addresses', 1, 'street'])
 * // 'users[0].addresses[1].street'
 * ```
 */
declare function stringifyFieldPath(path: Array<string | number>): string;

/**
 * @internal
 * Internal utility for calculating form control field paths.
 *
 * **Not intended for external use.** This function is used internally by the library
 * to determine field names for validation. Use the `name` attribute on your form controls
 * instead of relying on this function.
 *
 * Calculates the field name of a form control: Eg: addresses.shippingAddress.street
 * @param rootForm
 * @param control
 */
declare function getFormControlField(rootForm: FormGroup, control: AbstractControl): string;
/**
 * @internal
 * Internal utility for calculating form group field paths.
 *
 * **Not intended for external use.** This function is used internally by the library
 * to determine field names for nested form groups.
 *
 * Calcuates the field name of a form group Eg: addresses.shippingAddress
 * @param rootForm
 * @param control
 */
declare function getFormGroupField(rootForm: FormGroup, control: AbstractControl): string;
/**
 * @internal
 * Internal utility for merging form values with disabled field values.
 *
 * **Not intended for external use.** This function is used internally by the library
 * to include disabled field values in form submissions. Use Angular's `getRawValue()`
 * method on your form if you need to access disabled field values.
 *
 * This utility merges the value of the form with the raw value.
 * By doing this we can assure that we don't lose values of disabled form fields
 *
 * Security: Unsafe prototype-related keys (`__proto__`, `prototype`, `constructor`)
 * are skipped during recursive merge.
 * @param form
 */
declare function mergeValuesAndRawValues<T>(form: FormGroup): T;
declare function cloneDeep<T>(object: T): T;
/**
 * Sets a value in an object at the provided field path.
 *
 * Supports dot and bracket notation via `parseFieldPath()`.
 * Examples: `user.profile.name`, `addresses[0].street`.
 *
 * Security: If any path segment matches an unsafe prototype-related key
 * (`__proto__`, `prototype`, `constructor`), the write is ignored.
 *
 * @param obj - Target object to mutate.
 * @param path - Dot/bracket field path.
 * @param value - Value to assign at the resolved path.
 */
declare function setValueAtPath(obj: object, path: string, value: unknown): void;
/**
 * @deprecated Use {@link setValueAtPath} instead
 */
declare function set(obj: object, path: string, value: unknown): void;
/**
 * @internal
 * Internal utility for collecting all form errors by field path.
 *
 * **Not intended for external use.** This function is used internally by the library
 * to generate the form state. Use the `formState()` signal from the `scVestForm` directive
 * to access form errors in your components.
 *
 * Traverses the form and returns the errors by path
 * @param form
 */
declare function getAllFormErrors(form?: AbstractControl): Record<string, string[]>;

/**
 * Options for configuring debounced pending state behavior.
 */
type DebouncedPendingStateOptions = {
    /**
     * Delay in milliseconds before showing the pending message.
     * Prevents flashing for quick async validations.
     * @default 200
     */
    showAfter?: number;
    /**
     * Minimum display time in milliseconds once the pending message is shown.
     * Prevents flickering when validation completes shortly after being shown.
     * @default 500
     */
    minimumDisplay?: number;
};
/**
 * Accepts either a static options object or a reactive `Signal` (e.g. an
 * `input()` accessor) so consumers can update debounce timings at runtime
 * without recreating the pending-state machine.
 */
type DebouncedPendingStateOptionsInput = DebouncedPendingStateOptions | Signal<DebouncedPendingStateOptions>;
/**
 * Result of createDebouncedPendingState containing the debounced signal
 * and cleanup function.
 */
type DebouncedPendingStateResult = {
    /**
     * Signal that is true when the pending message should be shown.
     * This is debounced according to the provided options.
     */
    showPendingMessage: Signal<boolean>;
    /**
     * Optional cleanup function to cancel any pending timeouts.
     * Call this in ngOnDestroy if needed (though the effect cleanup handles most cases).
     */
    cleanup: () => void;
};
/**
 * Creates a debounced pending state signal that prevents flashing validation messages.
 *
 * This utility helps implement a better UX for async validations by:
 * 1. Delaying the display of "Validating..." messages until validation takes longer than `showAfter`ms (default: 200ms)
 * 2. Keeping the message visible for at least `minimumDisplay`ms (default: 500ms) once shown to prevent flickering
 *
 * @example
 * ```typescript
 * @Component({
 *   template: `
 *     @if (showPendingMessage()) {
 *       <div role="status" aria-live="polite">Validating…</div>
 *     }
 *   `
 * })
 * export class CustomControlWrapperComponent {
 *   protected readonly errorDisplay = inject(FormErrorDisplayDirective, { self: true });
 *
 *   // Create debounced pending state
 *   private readonly pendingState = createDebouncedPendingState(
 *     this.errorDisplay.isPending,
 *     { showAfter: 200, minimumDisplay: 500 }
 *   );
 *
 *   protected readonly showPendingMessage = this.pendingState.showPendingMessage;
 * }
 * ```
 *
 * @param isPending - Signal indicating whether async validation is currently pending
 * @param options - Configuration options for debouncing behavior
 * @returns Object containing the debounced showPendingMessage signal and cleanup function
 */
declare function createDebouncedPendingState(isPending: Signal<boolean>, options?: DebouncedPendingStateOptionsInput): DebouncedPendingStateResult;

/**
 * Validates a form value against a shape to catch typos in `name` or `ngModelGroup` attributes.
 *
 * **What it checks:**
 * - Extra properties: Keys in formValue that don't exist in shape (likely typos)
 * - Type mismatches: When formValue has an object but shape expects a primitive
 *
 * **What it does NOT check:**
 * - Missing properties: Keys in shape that don't exist in formValue
 *   (forms build incrementally with `NgxDeepPartial`, and `@if` conditionally renders fields)
 *
 * Only runs in development mode.
 *
 * @param formVal - The current form value
 * @param shape - The expected shape (created with `NgxDeepRequired<T>`)
 */
declare function validateShape<T extends Record<string, unknown>, U extends Record<string, unknown>>(formVal: T, shape: U): void;

/**
 * @internal
 * Internal utility for shallow equality checks.
 *
 * **Not intended for external use.** This function is used internally by the library
 * for performance-critical operations. Consider using your own comparison logic or
 * a library like lodash if you need shallow equality checks in your application.
 *
 * Optimized shallow equality check for objects.
 *
 * **Why this custom implementation is preferred:**
 * - **Performance**: Direct property comparison is significantly faster than JSON.stringify
 * - **Type Safety**: Handles null/undefined values correctly without serialization issues
 * - **Accuracy**: Doesn't suffer from JSON.stringify limitations (undefined values, functions, symbols)
 * - **Memory Efficient**: No temporary string creation or object serialization overhead
 *
 * **Use Cases:**
 * - Form value change detection where only top-level properties matter
 * - Quick object comparison in performance-critical code paths
 * - Validation triggers where deep comparison is unnecessary
 *
 * **Performance Comparison:**
 * ```typescript
 * /// ❌ Slow: JSON.stringify approach
 * JSON.stringify(obj1) === JSON.stringify(obj2)
 *
 * /// ✅ Fast: Direct property comparison
 * shallowEqual(obj1, obj2)
 * ```
 *
 * @param obj1 - First object to compare
 * @param obj2 - Second object to compare
 * @returns true if objects are shallowly equal (same keys and same values by reference)
 */
declare function shallowEqual(obj1: unknown, obj2: unknown): boolean;
/**
 * @internal
 * Internal utility for deep equality checks.
 *
 * **Not intended for external use.** This function is used internally by the library
 * for form value comparison and change detection. Consider using your own comparison
 * logic or a library like lodash if you need deep equality checks in your application.
 *
 * Fast deep equality check optimized for form values and Angular applications.
 *
 * **Why this custom implementation is preferred over alternatives:**
 *
 * **vs JSON.stringify():**
 * - **10-100x faster**: Direct comparison without string serialization overhead
 * - **Accurate**: Handles Date objects, RegExp, undefined values, and functions correctly
 * - **Memory efficient**: No temporary string creation or garbage collection pressure
 * - **Preserves semantics**: Maintains type information during comparison
 *
 * **vs structuredClone():**
 * - **Wrong purpose**: structuredClone creates copies, not comparisons
 * - **Performance**: Would require cloning both objects just to compare them
 * - **Memory waste**: Creates unnecessary deep copies, doubling memory usage
 * - **Still incomplete**: Even after cloning, you'd still need a comparison function
 *
 * **vs External libraries (lodash.isEqual, etc.):**
 * - **Bundle size**: Zero dependencies, smaller application bundles
 * - **Form-specific**: Optimized for common Angular form data patterns
 * - **Type safety**: Full TypeScript integration with strict typing
 * - **Performance**: Tailored algorithms for form value comparison use cases
 *
 * **Supported Data Types:**
 * - Primitives (string, number, boolean, null, undefined, symbol, bigint)
 * - Arrays (with recursive deep comparison)
 * - Plain objects (with recursive deep comparison)
 * - Date objects (by timestamp comparison)
 * - RegExp objects (by source and flags comparison)
 * - Set objects (reference equality only)
 * - Map objects (reference equality only)
 * - Functions (reference equality only — distinct function instances are never equal,
 *   even if their source code is identical)
 *
 * **Safety Features:**
 * - **Circular reference handling**: Tracks visited object pairs with `WeakMap<object, WeakSet<object>>`
 * - **Type coercion prevention**: Strict type checking before comparison
 * - **Null safety**: Proper handling of null and undefined values
 *
 * **Performance Characteristics:**
 * ```typescript
 * /// Performance comparison on typical form objects:
 * /// JSON.stringify:    ~100ms for complex nested forms
 * /// fastDeepEqual:     ~1-5ms for the same objects
 * ///
 * /// Memory usage:
 * /// JSON.stringify:    Creates temporary strings (high GC pressure)
 * /// fastDeepEqual:     One small WeakMap of visited object pairs is allocated lazily on
 * ///                    first nested-container descent; primitive-only comparisons allocate nothing.
 * ```
 *
 * **Typical Usage in Forms:**
 * ```typescript
 * /// Detect when form values actually change
 * distinctUntilChanged(fastDeepEqual)
 *
 * /// Prevent unnecessary re-renders
 * if (!fastDeepEqual(oldFormValue, newFormValue)) {
 *   updateUI();
 * }
 * ```
 *
 * @param obj1 - First object to compare
 * @param obj2 - Second object to compare
 *
 * Cyclic arrays and plain objects are compared structurally by tracking visited object
 * pairs. Distinct cyclic graphs with the same structure compare equal. `Date` and
 * `RegExp` values compare structurally. `Map` and `Set` values compare by reference
 * only, so distinct instances are considered different even if their contents match.
 *
 * @returns true if objects are deeply equal by value
 */
declare function fastDeepEqual(obj1: unknown, obj2: unknown): boolean;

/**
 * @deprecated Use NGX_ERROR_DISPLAY_MODE_TOKEN instead
 */
declare const SC_ERROR_DISPLAY_MODE_TOKEN: InjectionToken<ScErrorDisplayMode>;
/**
 * Injection token for configuring the default error display mode.
 * Values:
 * - 'on-blur': Show errors after field is touched/blurred
 * - 'on-submit': Show errors after form submission
 * - 'on-blur-or-submit': Show errors after blur or form submission (default)
 * - 'on-dirty': Show errors as soon as the field value changes
 * - 'always': Show errors immediately, even on pristine fields
 */
declare const NGX_ERROR_DISPLAY_MODE_TOKEN: InjectionToken<ScErrorDisplayMode>;
/**
 * Injection token for configuring the default warning display mode.
 * Values:
 * - 'on-touch': Show warnings after field is touched/blurred
 * - 'on-validated-or-touch': Show warnings after validation runs or field is touched (default)
 * - 'on-dirty': Show warnings as soon as the field value changes
 * - 'always': Show warnings immediately, even on pristine fields
 */
declare const NGX_WARNING_DISPLAY_MODE_TOKEN: InjectionToken<NgxWarningDisplayMode>;

/**
 * Injection token for configurable validation config debounce timing.
 *
 * This token allows you to configure the debounce time for validation config
 * dependencies at the application, route, or component level.
 *
 * @example
 * ```typescript
 * /// Global configuration
 * export const appConfig: ApplicationConfig = {
 *   providers: [
 *     {
 *       provide: NGX_VALIDATION_CONFIG_DEBOUNCE_TOKEN,
 *       useValue: 200
 *     }
 *   ]
 * };
 *
 * /// Per-route configuration
 * {
 *   path: 'checkout',
 *   component: CheckoutComponent,
 *   providers: [
 *     {
 *       provide: NGX_VALIDATION_CONFIG_DEBOUNCE_TOKEN,
 *       useValue: 50
 *     }
 *   ]
 * }
 *
 * /// Per-component override
 * @Component({
 *   providers: [
 *     {
 *       provide: NGX_VALIDATION_CONFIG_DEBOUNCE_TOKEN,
 *       useValue: 0 // for testing
 *     }
 *   ]
 * })
 * export class TestFormComponent {}
 * ```
 *
 * @default 100ms - Maintains backward compatibility with existing behavior
 */
declare const NGX_VALIDATION_CONFIG_DEBOUNCE_TOKEN: InjectionToken<number>;

/**
 * Signature of a deep-equality comparator. Must return `true` iff `a` and `b`
 * are considered equal for the purpose of change detection.
 */
type NgxEqualityFn = (a: unknown, b: unknown) => boolean;
/**
 * Injection token for the deep-equality function used internally by
 * {@link FormDirective} for change detection — `formValueChange`
 * `distinctUntilChanged`, the form↔model two-way sync effect, and the
 * `formState` signal's structural comparator.
 *
 * The default factory returns {@link fastDeepEqual}. Override this token to
 * plug in a smaller or differently-tuned comparator (e.g. `dequal/lite`,
 * `lodash.isEqual`) without forking the directive.
 *
 * @example Bring-your-own equality at the application level
 * ```ts
 * import { dequal } from 'dequal/lite';
 * import { NGX_EQUALITY_FN } from 'ngx-vest-forms';
 *
 * export const appConfig: ApplicationConfig = {
 *   providers: [
 *     { provide: NGX_EQUALITY_FN, useValue: dequal },
 *   ],
 * };
 * ```
 *
 * @example Per-component override (e.g. for tests)
 * ```ts
 * @Component({
 *   providers: [
 *     { provide: NGX_EQUALITY_FN, useValue: (a, b) => a === b },
 *   ],
 * })
 * export class TestFormComponent {}
 * ```
 *
 * @default {@link fastDeepEqual}
 */
declare const NGX_EQUALITY_FN: InjectionToken<NgxEqualityFn>;

export { ControlWrapperComponent, DEFAULT_FOCUS_SELECTOR, DEFAULT_INVALID_SELECTOR, FormControlStateDirective, FormDirective, FormErrorControlDirective, FormErrorDisplayDirective, FormGroupWrapperComponent, FormModelDirective, FormModelGroupDirective, NGX_EQUALITY_FN, NGX_ERROR_DISPLAY_MODE_TOKEN, NGX_VALIDATION_CONFIG_DEBOUNCE_TOKEN, NGX_WARNING_DISPLAY_MODE_TOKEN, NgxVestForms, ROOT_FORM, ROOT_FORM as ROOT_FORM_CONSTANT, SC_ERROR_DISPLAY_MODE_TOKEN, ValidateRootFormDirective, ValidationConfigBuilder, arrayToObject, clearFields, clearFieldsWhen, cloneDeep, createDebouncedPendingState, createEmptyFormState, createValidationConfig, deepArrayToObject, fastDeepEqual, getAllFormErrors, getFormControlField, getFormGroupField, keepFieldsWhen, mergeAriaDescribedBy, mergeValuesAndRawValues, objectToArray, parseAriaIdTokens, parseFieldPath, resolveAssociationTargets, set, setValueAtPath, shallowEqual, stringifyFieldPath, validateShape, vestForms, vestFormsViewProviders };
export type { AriaAssociationMode, DebouncedPendingStateOptions, DebouncedPendingStateOptionsInput, DebouncedPendingStateResult, DeepPartial, DeepRequired, FieldPath, FieldPathValue, FormCompatibleDeepRequired, FormFieldName, LeafFieldPath, NgxDeepPartial, NgxDeepRequired, NgxEqualityFn, NgxFieldBlurEvent, NgxFieldKey, NgxFirstInvalidOptions, NgxFormCompatibleDeepRequired, NgxFormState, NgxTypedVestSuite, NgxValidationConfig, NgxVestSuite, NgxWarningDisplayMode, ScErrorDisplayMode, ValidateFieldPath, ValidationConfigMap, ValidationOptions };
