/**
 * @license Angular v21.1.4
 * (c) 2010-2026 Google LLC. https://angular.dev/
 * License: MIT
 */

import * as i0 from '@angular/core';
import { InjectionToken, ɵRuntimeError as _RuntimeError, inject, ElementRef, Injector, input, computed, signal, ɵCONTROL as _CONTROL, untracked, effect, Directive, ɵɵcontrolCreate as __controlCreate, ɵcontrolUpdate as _controlUpdate, ɵisPromise as _isPromise, resource } from '@angular/core';
import { Validators, NG_VALUE_ACCESSOR, NgControl } from '@angular/forms';
import { assertPathIsCurrent, FieldPathNode, addDefaultField, metadata, createMetadataKey, MAX, MAX_LENGTH, MIN, MIN_LENGTH, PATTERN, REQUIRED, createManagedMetadataKey, DEBOUNCER } from './_structure-chunk.mjs';
export { MetadataKey, MetadataReducer, apply, applyEach, applyWhen, applyWhenValue, form, schema, submit } from './_structure-chunk.mjs';
import { httpResource } from '@angular/common/http';
import '@angular/core/primitives/signals';

const SIGNAL_FORMS_CONFIG = new InjectionToken(typeof ngDevMode !== 'undefined' && ngDevMode ? 'SIGNAL_FORMS_CONFIG' : '');

function provideSignalFormsConfig(config) {
  return [{
    provide: SIGNAL_FORMS_CONFIG,
    useValue: config
  }];
}

class InteropNgControl {
  field;
  constructor(field) {
    this.field = field;
  }
  control = this;
  get value() {
    return this.field().value();
  }
  get valid() {
    return this.field().valid();
  }
  get invalid() {
    return this.field().invalid();
  }
  get pending() {
    return this.field().pending();
  }
  get disabled() {
    return this.field().disabled();
  }
  get enabled() {
    return !this.field().disabled();
  }
  get errors() {
    const errors = this.field().errors();
    if (errors.length === 0) {
      return null;
    }
    const errObj = {};
    for (const error of errors) {
      errObj[error.kind] = error;
    }
    return errObj;
  }
  get pristine() {
    return !this.field().dirty();
  }
  get dirty() {
    return this.field().dirty();
  }
  get touched() {
    return this.field().touched();
  }
  get untouched() {
    return !this.field().touched();
  }
  get status() {
    if (this.field().disabled()) {
      return 'DISABLED';
    }
    if (this.field().valid()) {
      return 'VALID';
    }
    if (this.field().invalid()) {
      return 'INVALID';
    }
    if (this.field().pending()) {
      return 'PENDING';
    }
    throw new _RuntimeError(1910, ngDevMode && 'Unknown form control status');
  }
  valueAccessor = null;
  hasValidator(validator) {
    if (validator === Validators.required) {
      return this.field().required();
    }
    return false;
  }
  updateValueAndValidity() {}
}

const FORM_FIELD = new InjectionToken(typeof ngDevMode !== 'undefined' && ngDevMode ? 'FORM_FIELD' : '');
const controlInstructions = {
  create: __controlCreate,
  update: _controlUpdate
};
class FormField {
  element = inject(ElementRef).nativeElement;
  injector = inject(Injector);
  formField = input.required(...(ngDevMode ? [{
    debugName: "formField"
  }] : []));
  state = computed(() => this.formField()(), ...(ngDevMode ? [{
    debugName: "state"
  }] : []));
  bindingOptions = signal(undefined, ...(ngDevMode ? [{
    debugName: "bindingOptions"
  }] : []));
  [_CONTROL] = controlInstructions;
  config = inject(SIGNAL_FORMS_CONFIG, {
    optional: true
  });
  classes = Object.entries(this.config?.classes ?? {}).map(([className, computation]) => [className, computed(() => computation(this))]);
  controlValueAccessors = inject(NG_VALUE_ACCESSOR, {
    optional: true,
    self: true
  });
  interopNgControl;
  get ɵinteropControl() {
    return this.controlValueAccessors?.[0] ?? this.interopNgControl?.valueAccessor ?? undefined;
  }
  getOrCreateNgControl() {
    return this.interopNgControl ??= new InteropNgControl(this.state);
  }
  registerAsBinding(bindingOptions) {
    if (untracked(this.bindingOptions)) {
      throw new _RuntimeError(1913, ngDevMode && 'FormField already registered as a binding');
    }
    this.bindingOptions.set(bindingOptions);
    effect(onCleanup => {
      const fieldNode = this.state();
      fieldNode.nodeState.formFieldBindings.update(controls => [...controls, this]);
      onCleanup(() => {
        fieldNode.nodeState.formFieldBindings.update(controls => controls.filter(c => c !== this));
      });
    }, {
      injector: this.injector
    });
  }
  focus() {
    const bindingOptions = untracked(this.bindingOptions);
    if (bindingOptions?.focus) {
      bindingOptions.focus();
    } else {
      this.element.focus();
    }
  }
  static ɵfac = i0.ɵɵngDeclareFactory({
    minVersion: "12.0.0",
    version: "21.1.4",
    ngImport: i0,
    type: FormField,
    deps: [],
    target: i0.ɵɵFactoryTarget.Directive
  });
  static ɵdir = i0.ɵɵngDeclareDirective({
    minVersion: "17.1.0",
    version: "21.1.4",
    type: FormField,
    isStandalone: true,
    selector: "[formField]",
    inputs: {
      formField: {
        classPropertyName: "formField",
        publicName: "formField",
        isSignal: true,
        isRequired: true,
        transformFunction: null
      }
    },
    providers: [{
      provide: FORM_FIELD,
      useExisting: FormField
    }, {
      provide: NgControl,
      useFactory: () => inject(FormField).getOrCreateNgControl()
    }],
    ngImport: i0
  });
}
i0.ɵɵngDeclareClassMetadata({
  minVersion: "12.0.0",
  version: "21.1.4",
  ngImport: i0,
  type: FormField,
  decorators: [{
    type: Directive,
    args: [{
      selector: '[formField]',
      providers: [{
        provide: FORM_FIELD,
        useExisting: FormField
      }, {
        provide: NgControl,
        useFactory: () => inject(FormField).getOrCreateNgControl()
      }]
    }]
  }],
  propDecorators: {
    formField: [{
      type: i0.Input,
      args: [{
        isSignal: true,
        alias: "formField",
        required: true
      }]
    }]
  }
});

function disabled(path, logic) {
  assertPathIsCurrent(path);
  const pathNode = FieldPathNode.unwrapFieldPath(path);
  pathNode.builder.addDisabledReasonRule(ctx => {
    let result = true;
    if (typeof logic === 'string') {
      result = logic;
    } else if (logic) {
      result = logic(ctx);
    }
    if (typeof result === 'string') {
      return {
        fieldTree: ctx.fieldTree,
        message: result
      };
    }
    return result ? {
      fieldTree: ctx.fieldTree
    } : undefined;
  });
}

function hidden(path, logic) {
  assertPathIsCurrent(path);
  const pathNode = FieldPathNode.unwrapFieldPath(path);
  pathNode.builder.addHiddenRule(logic);
}

function readonly(path, logic = () => true) {
  assertPathIsCurrent(path);
  const pathNode = FieldPathNode.unwrapFieldPath(path);
  pathNode.builder.addReadonlyRule(logic);
}

function getLengthOrSize(value) {
  const v = value;
  return typeof v.length === 'number' ? v.length : v.size;
}
function getOption(opt, ctx) {
  return opt instanceof Function ? opt(ctx) : opt;
}
function isEmpty(value) {
  if (typeof value === 'number') {
    return isNaN(value);
  }
  return value === '' || value === false || value == null;
}

function validate(path, logic) {
  assertPathIsCurrent(path);
  const pathNode = FieldPathNode.unwrapFieldPath(path);
  pathNode.builder.addSyncErrorRule(ctx => {
    return addDefaultField(logic(ctx), ctx.fieldTree);
  });
}

function requiredError(options) {
  return new RequiredValidationError(options);
}
function minError(min, options) {
  return new MinValidationError(min, options);
}
function maxError(max, options) {
  return new MaxValidationError(max, options);
}
function minLengthError(minLength, options) {
  return new MinLengthValidationError(minLength, options);
}
function maxLengthError(maxLength, options) {
  return new MaxLengthValidationError(maxLength, options);
}
function patternError(pattern, options) {
  return new PatternValidationError(pattern, options);
}
function emailError(options) {
  return new EmailValidationError(options);
}
function standardSchemaError(issue, options) {
  return new StandardSchemaValidationError(issue, options);
}
class _NgValidationError {
  __brand = undefined;
  kind = '';
  fieldTree;
  message;
  constructor(options) {
    if (options) {
      Object.assign(this, options);
    }
  }
}
class RequiredValidationError extends _NgValidationError {
  kind = 'required';
}
class MinValidationError extends _NgValidationError {
  min;
  kind = 'min';
  constructor(min, options) {
    super(options);
    this.min = min;
  }
}
class MaxValidationError extends _NgValidationError {
  max;
  kind = 'max';
  constructor(max, options) {
    super(options);
    this.max = max;
  }
}
class MinLengthValidationError extends _NgValidationError {
  minLength;
  kind = 'minLength';
  constructor(minLength, options) {
    super(options);
    this.minLength = minLength;
  }
}
class MaxLengthValidationError extends _NgValidationError {
  maxLength;
  kind = 'maxLength';
  constructor(maxLength, options) {
    super(options);
    this.maxLength = maxLength;
  }
}
class PatternValidationError extends _NgValidationError {
  pattern;
  kind = 'pattern';
  constructor(pattern, options) {
    super(options);
    this.pattern = pattern;
  }
}
class EmailValidationError extends _NgValidationError {
  kind = 'email';
}
class StandardSchemaValidationError extends _NgValidationError {
  issue;
  kind = 'standardSchema';
  constructor(issue, options) {
    super(options);
    this.issue = issue;
  }
}
const NgValidationError = _NgValidationError;

const EMAIL_REGEXP = /^(?=.{1,254}$)(?=.{1,64}@)[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
function email(path, config) {
  validate(path, ctx => {
    if (isEmpty(ctx.value())) {
      return undefined;
    }
    if (!EMAIL_REGEXP.test(ctx.value())) {
      if (config?.error) {
        return getOption(config.error, ctx);
      } else {
        return emailError({
          message: getOption(config?.message, ctx)
        });
      }
    }
    return undefined;
  });
}

function max(path, maxValue, config) {
  const MAX_MEMO = metadata(path, createMetadataKey(), ctx => typeof maxValue === 'number' ? maxValue : maxValue(ctx));
  metadata(path, MAX, ({
    state
  }) => state.metadata(MAX_MEMO)());
  validate(path, ctx => {
    if (isEmpty(ctx.value())) {
      return undefined;
    }
    const max = ctx.state.metadata(MAX_MEMO)();
    if (max === undefined || Number.isNaN(max)) {
      return undefined;
    }
    const value = ctx.value();
    const numValue = !value && value !== 0 ? NaN : Number(value);
    if (numValue > max) {
      if (config?.error) {
        return getOption(config.error, ctx);
      } else {
        return maxError(max, {
          message: getOption(config?.message, ctx)
        });
      }
    }
    return undefined;
  });
}

function maxLength(path, maxLength, config) {
  const MAX_LENGTH_MEMO = metadata(path, createMetadataKey(), ctx => typeof maxLength === 'number' ? maxLength : maxLength(ctx));
  metadata(path, MAX_LENGTH, ({
    state
  }) => state.metadata(MAX_LENGTH_MEMO)());
  validate(path, ctx => {
    if (isEmpty(ctx.value())) {
      return undefined;
    }
    const maxLength = ctx.state.metadata(MAX_LENGTH_MEMO)();
    if (maxLength === undefined) {
      return undefined;
    }
    if (getLengthOrSize(ctx.value()) > maxLength) {
      if (config?.error) {
        return getOption(config.error, ctx);
      } else {
        return maxLengthError(maxLength, {
          message: getOption(config?.message, ctx)
        });
      }
    }
    return undefined;
  });
}

function min(path, minValue, config) {
  const MIN_MEMO = metadata(path, createMetadataKey(), ctx => typeof minValue === 'number' ? minValue : minValue(ctx));
  metadata(path, MIN, ({
    state
  }) => state.metadata(MIN_MEMO)());
  validate(path, ctx => {
    if (isEmpty(ctx.value())) {
      return undefined;
    }
    const min = ctx.state.metadata(MIN_MEMO)();
    if (min === undefined || Number.isNaN(min)) {
      return undefined;
    }
    const value = ctx.value();
    const numValue = !value && value !== 0 ? NaN : Number(value);
    if (numValue < min) {
      if (config?.error) {
        return getOption(config.error, ctx);
      } else {
        return minError(min, {
          message: getOption(config?.message, ctx)
        });
      }
    }
    return undefined;
  });
}

function minLength(path, minLength, config) {
  const MIN_LENGTH_MEMO = metadata(path, createMetadataKey(), ctx => typeof minLength === 'number' ? minLength : minLength(ctx));
  metadata(path, MIN_LENGTH, ({
    state
  }) => state.metadata(MIN_LENGTH_MEMO)());
  validate(path, ctx => {
    if (isEmpty(ctx.value())) {
      return undefined;
    }
    const minLength = ctx.state.metadata(MIN_LENGTH_MEMO)();
    if (minLength === undefined) {
      return undefined;
    }
    if (getLengthOrSize(ctx.value()) < minLength) {
      if (config?.error) {
        return getOption(config.error, ctx);
      } else {
        return minLengthError(minLength, {
          message: getOption(config?.message, ctx)
        });
      }
    }
    return undefined;
  });
}

function pattern(path, pattern, config) {
  const PATTERN_MEMO = metadata(path, createMetadataKey(), ctx => pattern instanceof RegExp ? pattern : pattern(ctx));
  metadata(path, PATTERN, ({
    state
  }) => state.metadata(PATTERN_MEMO)());
  validate(path, ctx => {
    if (isEmpty(ctx.value())) {
      return undefined;
    }
    const pattern = ctx.state.metadata(PATTERN_MEMO)();
    if (pattern === undefined) {
      return undefined;
    }
    if (!pattern.test(ctx.value())) {
      if (config?.error) {
        return getOption(config.error, ctx);
      } else {
        return patternError(pattern, {
          message: getOption(config?.message, ctx)
        });
      }
    }
    return undefined;
  });
}

function required(path, config) {
  const REQUIRED_MEMO = metadata(path, createMetadataKey(), ctx => config?.when ? config.when(ctx) : true);
  metadata(path, REQUIRED, ({
    state
  }) => state.metadata(REQUIRED_MEMO)());
  validate(path, ctx => {
    if (ctx.state.metadata(REQUIRED_MEMO)() && isEmpty(ctx.value())) {
      if (config?.error) {
        return getOption(config.error, ctx);
      } else {
        return requiredError({
          message: getOption(config?.message, ctx)
        });
      }
    }
    return undefined;
  });
}

function validateAsync(path, opts) {
  assertPathIsCurrent(path);
  const pathNode = FieldPathNode.unwrapFieldPath(path);
  const RESOURCE = createManagedMetadataKey(opts.factory);
  metadata(path, RESOURCE, ctx => {
    const node = ctx.stateOf(path);
    const validationState = node.validationState;
    if (validationState.shouldSkipValidation() || !validationState.syncValid()) {
      return undefined;
    }
    return opts.params(ctx);
  });
  pathNode.builder.addAsyncErrorRule(ctx => {
    const res = ctx.state.metadata(RESOURCE);
    let errors;
    switch (res.status()) {
      case 'idle':
        return undefined;
      case 'loading':
      case 'reloading':
        return 'pending';
      case 'resolved':
      case 'local':
        if (!res.hasValue()) {
          return undefined;
        }
        errors = opts.onSuccess(res.value(), ctx);
        return addDefaultField(errors, ctx.fieldTree);
      case 'error':
        errors = opts.onError(res.error(), ctx);
        return addDefaultField(errors, ctx.fieldTree);
    }
  });
}

function validateTree(path, logic) {
  assertPathIsCurrent(path);
  const pathNode = FieldPathNode.unwrapFieldPath(path);
  pathNode.builder.addSyncTreeErrorRule(ctx => addDefaultField(logic(ctx), ctx.fieldTree));
}

function validateStandardSchema(path, schema) {
  const VALIDATOR_MEMO = metadata(path, createMetadataKey(), ctx => {
    const resolvedSchema = typeof schema === 'function' ? schema(ctx) : schema;
    return resolvedSchema ? resolvedSchema['~standard'].validate(ctx.value()) : undefined;
  });
  validateTree(path, ({
    state,
    fieldTreeOf
  }) => {
    const result = state.metadata(VALIDATOR_MEMO)();
    if (!result || _isPromise(result)) {
      return [];
    }
    return result?.issues?.map(issue => standardIssueToFormTreeError(fieldTreeOf(path), issue)) ?? [];
  });
  validateAsync(path, {
    params: ({
      state
    }) => {
      const result = state.metadata(VALIDATOR_MEMO)();
      return result && _isPromise(result) ? result : undefined;
    },
    factory: params => {
      return resource({
        params,
        loader: async ({
          params
        }) => (await params)?.issues ?? []
      });
    },
    onSuccess: (issues, {
      fieldTreeOf
    }) => {
      return issues.map(issue => standardIssueToFormTreeError(fieldTreeOf(path), issue));
    },
    onError: () => {}
  });
}
function standardIssueToFormTreeError(fieldTree, issue) {
  let target = fieldTree;
  for (const pathPart of issue.path ?? []) {
    const pathKey = typeof pathPart === 'object' ? pathPart.key : pathPart;
    target = target[pathKey];
  }
  return addDefaultField(standardSchemaError(issue, {
    message: issue.message
  }), target);
}

function validateHttp(path, opts) {
  validateAsync(path, {
    params: opts.request,
    factory: request => httpResource(request, opts.options),
    onSuccess: opts.onSuccess,
    onError: opts.onError
  });
}

function debounce(path, durationOrDebouncer) {
  assertPathIsCurrent(path);
  const pathNode = FieldPathNode.unwrapFieldPath(path);
  const debouncer = typeof durationOrDebouncer === 'function' ? durationOrDebouncer : durationOrDebouncer > 0 ? debounceForDuration(durationOrDebouncer) : immediate;
  pathNode.builder.addMetadataRule(DEBOUNCER, () => debouncer);
}
function debounceForDuration(durationInMilliseconds) {
  return (_context, abortSignal) => {
    return new Promise(resolve => {
      let timeoutId;
      const onAbort = () => {
        clearTimeout(timeoutId);
        resolve();
      };
      timeoutId = setTimeout(() => {
        abortSignal.removeEventListener('abort', onAbort);
        resolve();
      }, durationInMilliseconds);
      abortSignal.addEventListener('abort', onAbort, {
        once: true
      });
    });
  };
}
function immediate() {}

export { EmailValidationError, FORM_FIELD, FormField, MAX, MAX_LENGTH, MIN, MIN_LENGTH, MaxLengthValidationError, MaxValidationError, MinLengthValidationError, MinValidationError, NgValidationError, PATTERN, PatternValidationError, REQUIRED, RequiredValidationError, StandardSchemaValidationError, createManagedMetadataKey, createMetadataKey, debounce, disabled, email, emailError, hidden, max, maxError, maxLength, maxLengthError, metadata, min, minError, minLength, minLengthError, pattern, patternError, provideSignalFormsConfig, readonly, required, requiredError, standardSchemaError, validate, validateAsync, validateHttp, validateStandardSchema, validateTree };
//# sourceMappingURL=signals.mjs.map
