@angular/forms
Version:
Angular - directives and services for creating forms
1 lines • 98.7 kB
Source Map (JSON)
{"version":3,"file":"signals.mjs","sources":["../../../../../k8-fastbuild-ST-fdfa778d11ba/bin/packages/forms/signals/src/field/di.ts","../../../../../k8-fastbuild-ST-fdfa778d11ba/bin/packages/forms/signals/src/api/di.ts","../../../../../k8-fastbuild-ST-fdfa778d11ba/bin/packages/forms/signals/src/controls/interop_ng_control.ts","../../../../../k8-fastbuild-ST-fdfa778d11ba/bin/packages/forms/signals/src/api/field_directive.ts","../../../../../k8-fastbuild-ST-fdfa778d11ba/bin/packages/forms/signals/src/api/rules/disabled.ts","../../../../../k8-fastbuild-ST-fdfa778d11ba/bin/packages/forms/signals/src/api/rules/hidden.ts","../../../../../k8-fastbuild-ST-fdfa778d11ba/bin/packages/forms/signals/src/api/rules/readonly.ts","../../../../../k8-fastbuild-ST-fdfa778d11ba/bin/packages/forms/signals/src/api/rules/validation/validation_errors.ts","../../../../../k8-fastbuild-ST-fdfa778d11ba/bin/packages/forms/signals/src/api/rules/validation/util.ts","../../../../../k8-fastbuild-ST-fdfa778d11ba/bin/packages/forms/signals/src/api/rules/validation/validate.ts","../../../../../k8-fastbuild-ST-fdfa778d11ba/bin/packages/forms/signals/src/api/rules/validation/email.ts","../../../../../k8-fastbuild-ST-fdfa778d11ba/bin/packages/forms/signals/src/api/rules/validation/max.ts","../../../../../k8-fastbuild-ST-fdfa778d11ba/bin/packages/forms/signals/src/api/rules/validation/max_length.ts","../../../../../k8-fastbuild-ST-fdfa778d11ba/bin/packages/forms/signals/src/api/rules/validation/min.ts","../../../../../k8-fastbuild-ST-fdfa778d11ba/bin/packages/forms/signals/src/api/rules/validation/min_length.ts","../../../../../k8-fastbuild-ST-fdfa778d11ba/bin/packages/forms/signals/src/api/rules/validation/pattern.ts","../../../../../k8-fastbuild-ST-fdfa778d11ba/bin/packages/forms/signals/src/api/rules/validation/required.ts","../../../../../k8-fastbuild-ST-fdfa778d11ba/bin/packages/forms/signals/src/api/rules/validation/validate_async.ts","../../../../../k8-fastbuild-ST-fdfa778d11ba/bin/packages/forms/signals/src/api/rules/validation/validate_tree.ts","../../../../../k8-fastbuild-ST-fdfa778d11ba/bin/packages/forms/signals/src/api/rules/validation/standard_schema.ts","../../../../../k8-fastbuild-ST-fdfa778d11ba/bin/packages/forms/signals/src/api/rules/validation/validate_http.ts","../../../../../k8-fastbuild-ST-fdfa778d11ba/bin/packages/forms/signals/src/api/rules/debounce.ts"],"sourcesContent":["/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {InjectionToken} from '@angular/core';\nimport type {SignalFormsConfig} from '../api/di';\n\n/** Injection token for the signal forms configuration. */\nexport const SIGNAL_FORMS_CONFIG = new InjectionToken<SignalFormsConfig>(\n typeof ngDevMode !== 'undefined' && ngDevMode ? 'SIGNAL_FORMS_CONFIG' : '',\n);\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {type Provider} from '@angular/core';\nimport {SIGNAL_FORMS_CONFIG} from '../field/di';\nimport type {Field} from './field_directive';\n\n/**\n * Configuration options for signal forms.\n *\n * @experimental 21.0.1\n */\nexport interface SignalFormsConfig {\n /** A map of CSS class names to predicate functions that determine when to apply them. */\n classes?: {[className: string]: (state: Field<unknown>) => boolean};\n}\n\n/**\n * Provides configuration options for signal forms.\n *\n * @experimental 21.0.1\n */\nexport function provideSignalFormsConfig(config: SignalFormsConfig): Provider[] {\n return [{provide: SIGNAL_FORMS_CONFIG, useValue: config}];\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {ɵRuntimeError as RuntimeError} from '@angular/core';\nimport {SignalFormsErrorCode} from '../errors';\n\nimport {\n ControlValueAccessor,\n Validators,\n type AbstractControl,\n type FormControlStatus,\n type NgControl,\n type ValidationErrors,\n type ValidatorFn,\n} from '@angular/forms';\nimport type {FieldState} from '../api/types';\n\n// TODO: Also consider supporting (if possible):\n// - hasError\n// - getError\n// - reset\n// - name\n// - path\n// - markAs[Touched,Dirty,etc.]\n\n/**\n * Properties of both NgControl & AbstractControl that are supported by the InteropNgControl.\n */\nexport type InteropSharedKeys =\n | 'value'\n | 'valid'\n | 'invalid'\n | 'touched'\n | 'untouched'\n | 'disabled'\n | 'enabled'\n | 'errors'\n | 'pristine'\n | 'dirty'\n | 'status';\n\n/**\n * A fake version of `NgControl` provided by the `Field` directive. This allows interoperability\n * with a wider range of components designed to work with reactive forms, in particular ones that\n * inject the `NgControl`. The interop control does not implement *all* properties and methods of\n * the real `NgControl`, but does implement some of the most commonly used ones that have a clear\n * equivalent in signal forms.\n */\nexport class InteropNgControl\n implements\n Pick<NgControl, InteropSharedKeys | 'control' | 'valueAccessor'>,\n Pick<AbstractControl<unknown>, InteropSharedKeys | 'hasValidator'>\n{\n constructor(protected field: () => FieldState<unknown>) {}\n\n readonly control: AbstractControl<any, any> = this as unknown as AbstractControl<any, any>;\n\n get value(): any {\n return this.field().value();\n }\n\n get valid(): boolean {\n return this.field().valid();\n }\n\n get invalid(): boolean {\n return this.field().invalid();\n }\n\n get pending(): boolean | null {\n return this.field().pending();\n }\n\n get disabled(): boolean {\n return this.field().disabled();\n }\n\n get enabled(): boolean {\n return !this.field().disabled();\n }\n\n get errors(): ValidationErrors | null {\n const errors = this.field().errors();\n if (errors.length === 0) {\n return null;\n }\n const errObj: ValidationErrors = {};\n for (const error of errors) {\n errObj[error.kind] = error;\n }\n return errObj;\n }\n\n get pristine(): boolean {\n return !this.field().dirty();\n }\n\n get dirty(): boolean {\n return this.field().dirty();\n }\n\n get touched(): boolean {\n return this.field().touched();\n }\n\n get untouched(): boolean {\n return !this.field().touched();\n }\n\n get status(): FormControlStatus {\n if (this.field().disabled()) {\n return 'DISABLED';\n }\n if (this.field().valid()) {\n return 'VALID';\n }\n if (this.field().invalid()) {\n return 'INVALID';\n }\n if (this.field().pending()) {\n return 'PENDING';\n }\n throw new RuntimeError(\n SignalFormsErrorCode.UNKNOWN_STATUS,\n ngDevMode && 'Unknown form control status',\n );\n }\n\n valueAccessor: ControlValueAccessor | null = null;\n\n hasValidator(validator: ValidatorFn): boolean {\n // This addresses a common case where users look for the presence of `Validators.required` to\n // determine whether or not to show a required \"*\" indicator in the UI.\n if (validator === Validators.required) {\n return this.field().required();\n }\n return false;\n }\n\n updateValueAndValidity() {\n // No-op since value and validity are always up to date in signal forms.\n // We offer this method so that reactive forms code attempting to call it doesn't error.\n }\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {\n computed,\n ɵɵcontrolCreate as createControlBinding,\n Directive,\n effect,\n ElementRef,\n inject,\n InjectionToken,\n Injector,\n input,\n ɵcontrolUpdate as updateControlBinding,\n ɵCONTROL,\n ɵInteropControl,\n type ɵControl,\n} from '@angular/core';\nimport {NG_VALUE_ACCESSOR, NgControl} from '@angular/forms';\nimport {InteropNgControl} from '../controls/interop_ng_control';\nimport {SIGNAL_FORMS_CONFIG} from '../field/di';\nimport type {FieldNode} from '../field/node';\nimport type {FieldTree} from './types';\n\n/**\n * Lightweight DI token provided by the {@link Field} directive.\n *\n * @category control\n * @experimental 21.0.0\n */\nexport const FIELD = new InjectionToken<Field<unknown>>(\n typeof ngDevMode !== 'undefined' && ngDevMode ? 'FIELD' : '',\n);\n\n/**\n * Instructions for dynamically binding a {@link Field} to a form control.\n */\nconst controlInstructions = {\n create: createControlBinding,\n update: updateControlBinding,\n} as const;\n\n/**\n * Binds a form `FieldTree` to a UI control that edits it. A UI control can be one of several things:\n * 1. A native HTML input or textarea\n * 2. A signal forms custom control that implements `FormValueControl` or `FormCheckboxControl`\n * 3. A component that provides a `ControlValueAccessor`. This should only be used for backwards\n * compatibility with reactive forms. Prefer options (1) and (2).\n *\n * This directive has several responsibilities:\n * 1. Two-way binds the field's value with the UI control's value\n * 2. Binds additional forms related state on the field to the UI control (disabled, required, etc.)\n * 3. Relays relevant events on the control to the field (e.g. marks field touched on blur)\n * 4. Provides a fake `NgControl` that implements a subset of the features available on the\n * reactive forms `NgControl`. This is provided to improve interoperability with controls\n * designed to work with reactive forms. It should not be used by controls written for signal\n * forms.\n *\n * @category control\n * @experimental 21.0.0\n */\n@Directive({\n selector: '[field]',\n providers: [\n {provide: FIELD, useExisting: Field},\n {provide: NgControl, useFactory: () => inject(Field).getOrCreateNgControl()},\n ],\n})\n// This directive should `implements ɵControl<T>`, but actually adding that breaks people's\n// builds because part of the public API is marked `@internal` and stripped.\n// Instead we have an type check below that enforces this in a non-breaking way.\nexport class Field<T> {\n readonly element = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n readonly injector = inject(Injector);\n readonly field = input.required<FieldTree<T>>();\n readonly state = computed(() => this.field()());\n\n readonly [ɵCONTROL] = controlInstructions;\n\n private config = inject(SIGNAL_FORMS_CONFIG, {optional: true});\n /** @internal */\n readonly classes = Object.entries(this.config?.classes ?? {}).map(\n ([className, computation]) =>\n [className, computed(() => computation(this as Field<unknown>))] as const,\n );\n\n /** Any `ControlValueAccessor` instances provided on the host element. */\n private readonly controlValueAccessors = inject(NG_VALUE_ACCESSOR, {optional: true, self: true});\n\n /** A lazily instantiated fake `NgControl`. */\n private interopNgControl: InteropNgControl | undefined;\n\n /**\n * A `ControlValueAccessor`, if configured, for the host component.\n *\n * @internal\n */\n get ɵinteropControl(): ɵInteropControl | undefined {\n return this.controlValueAccessors?.[0] ?? this.interopNgControl?.valueAccessor ?? undefined;\n }\n\n /** Lazily instantiates a fake `NgControl` for this field. */\n protected getOrCreateNgControl(): InteropNgControl {\n return (this.interopNgControl ??= new InteropNgControl(this.state));\n }\n\n /** @internal */\n ɵregister() {\n // Register this control on the field it is currently bound to. We do this at the end of\n // initialization so that it only runs if we are actually syncing with this control\n // (as opposed to just passing the field through to its `field` input).\n effect(\n (onCleanup) => {\n const fieldNode = this.state() as unknown as FieldNode;\n fieldNode.nodeState.fieldBindings.update((controls) => [\n ...controls,\n this as Field<unknown>,\n ]);\n onCleanup(() => {\n fieldNode.nodeState.fieldBindings.update((controls) =>\n controls.filter((c) => c !== this),\n );\n });\n },\n {injector: this.injector},\n );\n }\n}\n\n// We can't add `implements ɵControl<T>` to `Field` even though it should conform to the interface.\n// Instead we enforce it here through some utility types.\ntype Check<T extends true> = T;\ntype FieldImplementsɵControl = Check<Field<any> extends ɵControl<any> ? true : false>;\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {FieldPathNode} from '../../schema/path_node';\nimport {assertPathIsCurrent} from '../../schema/schema';\nimport type {FieldContext, LogicFn, PathKind, SchemaPath, SchemaPathRules} from '../types';\n\n/**\n * Adds logic to a field to conditionally disable it. A disabled field does not contribute to the\n * validation, touched/dirty, or other state of its parent field.\n *\n * @param path The target path to add the disabled logic to.\n * @param logic A reactive function that returns `true` (or a string reason) when the field is disabled,\n * and `false` when it is not disabled.\n * @template TValue The type of value stored in the field the logic is bound to.\n * @template TPathKind The kind of path the logic is bound to (a root path, child path, or item of an array)\n *\n * @category logic\n * @experimental 21.0.0\n */\nexport function disabled<TValue, TPathKind extends PathKind = PathKind.Root>(\n path: SchemaPath<TValue, SchemaPathRules.Supported, TPathKind>,\n logic?: string | NoInfer<LogicFn<TValue, boolean | string, TPathKind>>,\n): void {\n assertPathIsCurrent(path);\n\n const pathNode = FieldPathNode.unwrapFieldPath(path);\n pathNode.builder.addDisabledReasonRule((ctx) => {\n let result: boolean | string = true;\n if (typeof logic === 'string') {\n result = logic;\n } else if (logic) {\n result = logic(ctx as FieldContext<TValue, TPathKind>);\n }\n if (typeof result === 'string') {\n return {fieldTree: ctx.fieldTree, message: result};\n }\n return result ? {fieldTree: ctx.fieldTree} : undefined;\n });\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {FieldPathNode} from '../../schema/path_node';\nimport {assertPathIsCurrent} from '../../schema/schema';\nimport type {LogicFn, PathKind, SchemaPath, SchemaPathRules} from '../types';\n\n/**\n * Adds logic to a field to conditionally hide it. A hidden field does not contribute to the\n * validation, touched/dirty, or other state of its parent field.\n *\n * If a field may be hidden it is recommended to guard it with an `@if` in the template:\n * ```\n * @if (!email().hidden()) {\n * <label for=\"email\">Email</label>\n * <input id=\"email\" type=\"email\" [control]=\"email\" />\n * }\n * ```\n *\n * @param path The target path to add the hidden logic to.\n * @param logic A reactive function that returns `true` when the field is hidden.\n * @template TValue The type of value stored in the field the logic is bound to.\n * @template TPathKind The kind of path the logic is bound to (a root path, child path, or item of an array)\n *\n * @category logic\n * @experimental 21.0.0\n */\nexport function hidden<TValue, TPathKind extends PathKind = PathKind.Root>(\n path: SchemaPath<TValue, SchemaPathRules.Supported, TPathKind>,\n logic: NoInfer<LogicFn<TValue, boolean, TPathKind>>,\n): void {\n assertPathIsCurrent(path);\n\n const pathNode = FieldPathNode.unwrapFieldPath(path);\n pathNode.builder.addHiddenRule(logic);\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {FieldPathNode} from '../../schema/path_node';\nimport {assertPathIsCurrent} from '../../schema/schema';\nimport type {LogicFn, PathKind, SchemaPath, SchemaPathRules} from '../types';\n\n/**\n * Adds logic to a field to conditionally make it readonly. A readonly field does not contribute to\n * the validation, touched/dirty, or other state of its parent field.\n *\n * @param path The target path to make readonly.\n * @param logic A reactive function that returns `true` when the field is readonly.\n * @template TValue The type of value stored in the field the logic is bound to.\n * @template TPathKind The kind of path the logic is bound to (a root path, child path, or item of an array)\n *\n * @category logic\n * @experimental 21.0.0\n */\nexport function readonly<TValue, TPathKind extends PathKind = PathKind.Root>(\n path: SchemaPath<TValue, SchemaPathRules.Supported, TPathKind>,\n logic: NoInfer<LogicFn<TValue, boolean, TPathKind>> = () => true,\n) {\n assertPathIsCurrent(path);\n\n const pathNode = FieldPathNode.unwrapFieldPath(path);\n pathNode.builder.addReadonlyRule(logic);\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport type {StandardSchemaV1} from '@standard-schema/spec';\nimport {FieldTree} from '../../types';\n\n/**\n * Options used to create a `ValidationError`.\n */\ninterface ValidationErrorOptions {\n /** Human readable error message. */\n message?: string;\n}\n\n/**\n * A type that requires the given type `T` to have a `field` property.\n * @template T The type to add a `field` to.\n *\n * @experimental 21.0.0\n */\nexport type WithField<T> = T & {fieldTree: FieldTree<unknown>};\n\n/**\n * A type that allows the given type `T` to optionally have a `field` property.\n * @template T The type to optionally add a `field` to.\n *\n * @experimental 21.0.0\n */\nexport type WithOptionalField<T> = Omit<T, 'fieldTree'> & {fieldTree?: FieldTree<unknown>};\n\n/**\n * A type that ensures the given type `T` does not have a `field` property.\n * @template T The type to remove the `field` from.\n *\n * @experimental 21.0.0\n */\nexport type WithoutField<T> = T & {fieldTree: never};\n\n/**\n * Create a required error associated with the target field\n * @param options The validation error options\n *\n * @experimental 21.0.0\n */\nexport function requiredError(options: WithField<ValidationErrorOptions>): RequiredValidationError;\n/**\n * Create a required error\n * @param options The optional validation error options\n *\n * @category validation\n * @experimental 21.0.0\n */\nexport function requiredError(\n options?: ValidationErrorOptions,\n): WithoutField<RequiredValidationError>;\nexport function requiredError(\n options?: ValidationErrorOptions,\n): WithOptionalField<RequiredValidationError> {\n return new RequiredValidationError(options);\n}\n\n/**\n * Create a min value error associated with the target field\n * @param min The min value constraint\n * @param options The validation error options\n *\n * @category validation\n * @experimental 21.0.0\n */\nexport function minError(\n min: number,\n options: WithField<ValidationErrorOptions>,\n): MinValidationError;\n/**\n * Create a min value error\n * @param min The min value constraint\n * @param options The optional validation error options\n *\n * @category validation\n * @experimental 21.0.0\n */\nexport function minError(\n min: number,\n options?: ValidationErrorOptions,\n): WithoutField<MinValidationError>;\nexport function minError(\n min: number,\n options?: ValidationErrorOptions,\n): WithOptionalField<MinValidationError> {\n return new MinValidationError(min, options);\n}\n\n/**\n * Create a max value error associated with the target field\n * @param max The max value constraint\n * @param options The validation error options\n *\n * @category validation\n * @experimental 21.0.0\n */\nexport function maxError(\n max: number,\n options: WithField<ValidationErrorOptions>,\n): MaxValidationError;\n/**\n * Create a max value error\n * @param max The max value constraint\n * @param options The optional validation error options\n *\n * @category validation\n * @experimental 21.0.0\n */\nexport function maxError(\n max: number,\n options?: ValidationErrorOptions,\n): WithoutField<MaxValidationError>;\nexport function maxError(\n max: number,\n options?: ValidationErrorOptions,\n): WithOptionalField<MaxValidationError> {\n return new MaxValidationError(max, options);\n}\n\n/**\n * Create a minLength error associated with the target field\n * @param minLength The minLength constraint\n * @param options The validation error options\n *\n * @category validation\n * @experimental 21.0.0\n */\nexport function minLengthError(\n minLength: number,\n options: WithField<ValidationErrorOptions>,\n): MinLengthValidationError;\n/**\n * Create a minLength error\n * @param minLength The minLength constraint\n * @param options The optional validation error options\n *\n * @category validation\n * @experimental 21.0.0\n */\nexport function minLengthError(\n minLength: number,\n options?: ValidationErrorOptions,\n): WithoutField<MinLengthValidationError>;\nexport function minLengthError(\n minLength: number,\n options?: ValidationErrorOptions,\n): WithOptionalField<MinLengthValidationError> {\n return new MinLengthValidationError(minLength, options);\n}\n\n/**\n * Create a maxLength error associated with the target field\n * @param maxLength The maxLength constraint\n * @param options The validation error options\n *\n * @category validation\n * @experimental 21.0.0\n */\nexport function maxLengthError(\n maxLength: number,\n options: WithField<ValidationErrorOptions>,\n): MaxLengthValidationError;\n/**\n * Create a maxLength error\n * @param maxLength The maxLength constraint\n * @param options The optional validation error options\n *\n * @category validation\n * @experimental 21.0.0\n */\nexport function maxLengthError(\n maxLength: number,\n options?: ValidationErrorOptions,\n): WithoutField<MaxLengthValidationError>;\nexport function maxLengthError(\n maxLength: number,\n options?: ValidationErrorOptions,\n): WithOptionalField<MaxLengthValidationError> {\n return new MaxLengthValidationError(maxLength, options);\n}\n\n/**\n * Create a pattern matching error associated with the target field\n * @param pattern The violated pattern\n * @param options The validation error options\n *\n * @category validation\n * @experimental 21.0.0\n */\nexport function patternError(\n pattern: RegExp,\n options: WithField<ValidationErrorOptions>,\n): PatternValidationError;\n/**\n * Create a pattern matching error\n * @param pattern The violated pattern\n * @param options The optional validation error options\n *\n * @category validation\n * @experimental 21.0.0\n */\nexport function patternError(\n pattern: RegExp,\n options?: ValidationErrorOptions,\n): WithoutField<PatternValidationError>;\nexport function patternError(\n pattern: RegExp,\n options?: ValidationErrorOptions,\n): WithOptionalField<PatternValidationError> {\n return new PatternValidationError(pattern, options);\n}\n\n/**\n * Create an email format error associated with the target field\n * @param options The validation error options\n *\n * @category validation\n * @experimental 21.0.0\n */\nexport function emailError(options: WithField<ValidationErrorOptions>): EmailValidationError;\n/**\n * Create an email format error\n * @param options The optional validation error options\n *\n * @category validation\n * @experimental 21.0.0\n */\nexport function emailError(options?: ValidationErrorOptions): WithoutField<EmailValidationError>;\nexport function emailError(\n options?: ValidationErrorOptions,\n): WithOptionalField<EmailValidationError> {\n return new EmailValidationError(options);\n}\n\n/**\n * Create a standard schema issue error associated with the target field\n * @param issue The standard schema issue\n * @param options The validation error options\n *\n * @category validation\n * @experimental 21.0.0\n */\nexport function standardSchemaError(\n issue: StandardSchemaV1.Issue,\n options: WithField<ValidationErrorOptions>,\n): StandardSchemaValidationError;\n/**\n * Create a standard schema issue error\n * @param issue The standard schema issue\n * @param options The optional validation error options\n *\n * @category validation\n * @experimental 21.0.0\n */\nexport function standardSchemaError(\n issue: StandardSchemaV1.Issue,\n options?: ValidationErrorOptions,\n): WithoutField<StandardSchemaValidationError>;\nexport function standardSchemaError(\n issue: StandardSchemaV1.Issue,\n options?: ValidationErrorOptions,\n): WithOptionalField<StandardSchemaValidationError> {\n return new StandardSchemaValidationError(issue, options);\n}\n\n/**\n * Create a custom error associated with the target field\n * @param obj The object to create an error from\n *\n * @category validation\n * @experimental 21.0.0\n */\nexport function customError<E extends Partial<ValidationError.WithField>>(\n obj: WithField<E>,\n): CustomValidationError;\n/**\n * Create a custom error\n * @param obj The object to create an error from\n *\n * @category validation\n * @experimental 21.0.0\n */\nexport function customError<E extends Partial<ValidationError.WithField>>(\n obj?: E,\n): WithoutField<CustomValidationError>;\nexport function customError<E extends Partial<ValidationError.WithField>>(\n obj?: E,\n): WithOptionalField<CustomValidationError> {\n return new CustomValidationError(obj);\n}\n\n/**\n * Common interface for all validation errors.\n *\n * This can be returned from validators.\n *\n * It's also used by the creation functions to create an instance\n * (e.g. `requiredError`, `minError`, etc.).\n *\n * @category validation\n * @experimental 21.0.0\n */\nexport interface ValidationError {\n /** Identifies the kind of error. */\n readonly kind: string;\n /** Human readable error message. */\n readonly message?: string;\n}\n\nexport declare namespace ValidationError {\n /**\n * Validation error with a field.\n *\n * This is returned from field state, e.g., catField.errors() would be of a list of errors with\n * `field: catField` bound to state.\n */\n export interface WithField extends ValidationError {\n /** The field associated with this error. */\n readonly fieldTree: FieldTree<unknown>;\n }\n\n /**\n * Validation error with optional field.\n *\n * This is generally used in places where the result might have a field.\n * e.g., as a result of a `validateTree`, or when handling form submission.\n */\n export interface WithOptionalField extends ValidationError {\n /** The field associated with this error. */\n readonly fieldTree?: FieldTree<unknown>;\n }\n\n /**\n * Validation error with no field.\n *\n * This is used to strongly enforce that fields are not allowed in validation result.\n */\n export interface WithoutField extends ValidationError {\n /** The field associated with this error. */\n readonly field?: never;\n }\n}\n\n/**\n * A custom error that may contain additional properties\n *\n * @category validation\n * @experimental 21.0.0\n */\nexport class CustomValidationError implements ValidationError {\n /** Brand the class to avoid Typescript structural matching */\n private __brand = undefined;\n\n /**\n * Allow the user to attach arbitrary other properties.\n */\n [key: PropertyKey]: unknown;\n\n /** Identifies the kind of error. */\n readonly kind: string = '';\n\n /** The field associated with this error. */\n readonly fieldTree!: FieldTree<unknown>;\n\n /** Human readable error message. */\n readonly message?: string;\n\n constructor(options?: ValidationErrorOptions) {\n if (options) {\n Object.assign(this, options);\n }\n }\n}\n\n/**\n * Internal version of `NgValidationError`, we create this separately so we can change its type on\n * the exported version to a type union of the possible sub-classes.\n *\n * @experimental 21.0.0\n */\nabstract class _NgValidationError implements ValidationError {\n /** Brand the class to avoid Typescript structural matching */\n private __brand = undefined;\n\n /** Identifies the kind of error. */\n readonly kind: string = '';\n\n /** The field associated with this error. */\n readonly fieldTree!: FieldTree<unknown>;\n\n /** Human readable error message. */\n readonly message?: string;\n\n constructor(options?: ValidationErrorOptions) {\n if (options) {\n Object.assign(this, options);\n }\n }\n}\n\n/**\n * An error used to indicate that a required field is empty.\n *\n * @category validation\n * @experimental 21.0.0\n */\nexport class RequiredValidationError extends _NgValidationError {\n override readonly kind = 'required';\n}\n\n/**\n * An error used to indicate that a value is lower than the minimum allowed.\n *\n * @category validation\n * @experimental 21.0.0\n */\nexport class MinValidationError extends _NgValidationError {\n override readonly kind = 'min';\n\n constructor(\n readonly min: number,\n options?: ValidationErrorOptions,\n ) {\n super(options);\n }\n}\n\n/**\n * An error used to indicate that a value is higher than the maximum allowed.\n *\n * @category validation\n * @experimental 21.0.0\n */\nexport class MaxValidationError extends _NgValidationError {\n override readonly kind = 'max';\n\n constructor(\n readonly max: number,\n options?: ValidationErrorOptions,\n ) {\n super(options);\n }\n}\n\n/**\n * An error used to indicate that a value is shorter than the minimum allowed length.\n *\n * @category validation\n * @experimental 21.0.0\n */\nexport class MinLengthValidationError extends _NgValidationError {\n override readonly kind = 'minLength';\n\n constructor(\n readonly minLength: number,\n options?: ValidationErrorOptions,\n ) {\n super(options);\n }\n}\n\n/**\n * An error used to indicate that a value is longer than the maximum allowed length.\n *\n * @category validation\n * @experimental 21.0.0\n */\nexport class MaxLengthValidationError extends _NgValidationError {\n override readonly kind = 'maxLength';\n\n constructor(\n readonly maxLength: number,\n options?: ValidationErrorOptions,\n ) {\n super(options);\n }\n}\n\n/**\n * An error used to indicate that a value does not match the required pattern.\n *\n * @category validation\n * @experimental 21.0.0\n */\nexport class PatternValidationError extends _NgValidationError {\n override readonly kind = 'pattern';\n\n constructor(\n readonly pattern: RegExp,\n options?: ValidationErrorOptions,\n ) {\n super(options);\n }\n}\n\n/**\n * An error used to indicate that a value is not a valid email.\n *\n * @category validation\n * @experimental 21.0.0\n */\nexport class EmailValidationError extends _NgValidationError {\n override readonly kind = 'email';\n}\n\n/**\n * An error used to indicate an issue validating against a standard schema.\n *\n * @category validation\n * @experimental 21.0.0\n */\nexport class StandardSchemaValidationError extends _NgValidationError {\n override readonly kind = 'standardSchema';\n\n constructor(\n readonly issue: StandardSchemaV1.Issue,\n options?: ValidationErrorOptions,\n ) {\n super(options);\n }\n}\n\n/**\n * The base class for all built-in, non-custom errors. This class can be used to check if an error\n * is one of the standard kinds, allowing you to switch on the kind to further narrow the type.\n *\n * @example\n * ```ts\n * const f = form(...);\n * for (const e of form().errors()) {\n * if (e instanceof NgValidationError) {\n * switch(e.kind) {\n * case 'required':\n * console.log('This is required!');\n * break;\n * case 'min':\n * console.log(`Must be at least ${e.min}`);\n * break;\n * ...\n * }\n * }\n * }\n * ```\n *\n * @category validation\n * @experimental 21.0.0\n */\nexport const NgValidationError: abstract new () => NgValidationError = _NgValidationError as any;\nexport type NgValidationError =\n | RequiredValidationError\n | MinValidationError\n | MaxValidationError\n | MinLengthValidationError\n | MaxLengthValidationError\n | PatternValidationError\n | EmailValidationError\n | StandardSchemaValidationError;\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {isArray} from '../../../util/type_guards';\nimport {LogicFn, OneOrMany, PathKind, ValidationResult, type FieldContext} from '../../types';\nimport {customError, ValidationError} from './validation_errors';\n\n/** Represents a value that has a length or size, such as an array or string, or set. */\nexport type ValueWithLengthOrSize = {length: number} | {size: number};\n\n/** Common options available on the standard validators. */\nexport type BaseValidatorConfig<TValue, TPathKind extends PathKind = PathKind.Root> =\n | {\n /** A user-facing error message to include with the error. */\n message?: string | LogicFn<TValue, string, TPathKind>;\n error?: never;\n }\n | {\n /**\n * Custom validation error(s) to report instead of the default,\n * or a function that receives the `FieldContext` and returns custom validation error(s).\n */\n error?: OneOrMany<ValidationError> | LogicFn<TValue, OneOrMany<ValidationError>, TPathKind>;\n message?: never;\n };\n\n/** Gets the length or size of the given value. */\nexport function getLengthOrSize(value: ValueWithLengthOrSize) {\n const v = value as {length: number; size: number};\n return typeof v.length === 'number' ? v.length : v.size;\n}\n\n/**\n * Gets the value for an option that may be either a static value or a logic function that produces\n * the option value.\n *\n * @param opt The option from BaseValidatorConfig.\n * @param ctx The current FieldContext.\n * @returns The value for the option.\n */\nexport function getOption<TOption, TValue, TPathKind extends PathKind = PathKind.Root>(\n opt: Exclude<TOption, Function> | LogicFn<TValue, TOption, TPathKind> | undefined,\n ctx: FieldContext<TValue, TPathKind>,\n): TOption | undefined {\n return opt instanceof Function ? opt(ctx) : opt;\n}\n\n/**\n * Checks if the given value is considered empty. Empty values are: null, undefined, '', false, NaN.\n */\nexport function isEmpty(value: unknown): boolean {\n if (typeof value === 'number') {\n return isNaN(value);\n }\n return value === '' || value === false || value == null;\n}\n\n/**\n * Whether the value is a plain object, as opposed to being an instance of Validation error.\n * @param error An error that could be a plain object, or an instance of a class implementing ValidationError.\n */\nfunction isPlainError(error: ValidationError) {\n return (\n typeof error === 'object' &&\n (Object.getPrototypeOf(error) === Object.prototype || Object.getPrototypeOf(error) === null)\n );\n}\n\n/**\n * If the value provided is a plain object, it wraps it into a custom error.\n * @param error An error that could be a plain object, or an instance of a class implementing ValidationError.\n */\nfunction ensureCustomValidationError(error: ValidationError.WithField): ValidationError.WithField {\n if (isPlainError(error)) {\n return customError(error);\n }\n return error;\n}\n\n/**\n * Makes sure every provided error is wrapped as a custom error.\n * @param result Validation result with a field.\n */\nexport function ensureCustomValidationResult(\n result: ValidationResult<ValidationError.WithField>,\n): ValidationResult<ValidationError.WithField> {\n if (result === null || result === undefined) {\n return result;\n }\n\n if (isArray(result)) {\n return result.map(ensureCustomValidationError);\n }\n\n return ensureCustomValidationError(result);\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {addDefaultField} from '../../../field/validation';\nimport {FieldPathNode} from '../../../schema/path_node';\nimport {assertPathIsCurrent} from '../../../schema/schema';\nimport type {\n FieldContext,\n FieldValidator,\n PathKind,\n SchemaPath,\n SchemaPathRules,\n} from '../../types';\nimport {ensureCustomValidationResult} from './util';\n\n/**\n * Adds logic to a field to determine if the field has validation errors.\n *\n * @param path The target path to add the validation logic to.\n * @param logic A `Validator` that returns the current validation errors.\n * @template TValue The type of value stored in the field the logic is bound to.\n * @template TPathKind The kind of path the logic is bound to (a root path, child path, or item of an array)\n *\n * @category logic\n * @experimental 21.0.0\n */\nexport function validate<TValue, TPathKind extends PathKind = PathKind.Root>(\n path: SchemaPath<TValue, SchemaPathRules.Supported, TPathKind>,\n logic: NoInfer<FieldValidator<TValue, TPathKind>>,\n): void {\n assertPathIsCurrent(path);\n\n const pathNode = FieldPathNode.unwrapFieldPath(path);\n pathNode.builder.addSyncErrorRule((ctx) => {\n return ensureCustomValidationResult(\n addDefaultField(logic(ctx as FieldContext<TValue, TPathKind>), ctx.fieldTree),\n );\n });\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {PathKind, SchemaPath, SchemaPathRules} from '../../types';\nimport {BaseValidatorConfig, getOption, isEmpty} from './util';\nimport {validate} from './validate';\nimport {emailError} from './validation_errors';\n\n/**\n * A regular expression that matches valid e-mail addresses.\n *\n * At a high level, this regexp matches e-mail addresses of the format `local-part@tld`, where:\n * - `local-part` consists of one or more of the allowed characters (alphanumeric and some\n * punctuation symbols).\n * - `local-part` cannot begin or end with a period (`.`).\n * - `local-part` cannot be longer than 64 characters.\n * - `tld` consists of one or more `labels` separated by periods (`.`). For example `localhost` or\n * `foo.com`.\n * - A `label` consists of one or more of the allowed characters (alphanumeric, dashes (`-`) and\n * periods (`.`)).\n * - A `label` cannot begin or end with a dash (`-`) or a period (`.`).\n * - A `label` cannot be longer than 63 characters.\n * - The whole address cannot be longer than 254 characters.\n *\n * ## Implementation background\n *\n * This regexp was ported over from AngularJS (see there for git history):\n * https://github.com/angular/angular.js/blob/c133ef836/src/ng/directive/input.js#L27\n * It is based on the\n * [WHATWG version](https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address) with\n * some enhancements to incorporate more RFC rules (such as rules related to domain names and the\n * lengths of different parts of the address). The main differences from the WHATWG version are:\n * - Disallow `local-part` to begin or end with a period (`.`).\n * - Disallow `local-part` length to exceed 64 characters.\n * - Disallow total address length to exceed 254 characters.\n *\n * See [this commit](https://github.com/angular/angular.js/commit/f3f5cf72e) for more details.\n */\nconst EMAIL_REGEXP =\n /^(?=.{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])?)*$/;\n\n/**\n * Binds a validator to the given path that requires the value to match the standard email format.\n * This function can only be called on string paths.\n *\n * @param path Path of the field to validate\n * @param config Optional, allows providing any of the following options:\n * - `error`: Custom validation error(s) to be used instead of the default `ValidationError.email()`\n * or a function that receives the `FieldContext` and returns custom validation error(s).\n * @template TPathKind The kind of path the logic is bound to (a root path, child path, or item of an array)\n *\n * @category validation\n * @experimental 21.0.0\n */\nexport function email<TPathKind extends PathKind = PathKind.Root>(\n path: SchemaPath<string, SchemaPathRules.Supported, TPathKind>,\n config?: BaseValidatorConfig<string, TPathKind>,\n) {\n validate(path, (ctx) => {\n if (isEmpty(ctx.value())) {\n return undefined;\n }\n if (!EMAIL_REGEXP.test(ctx.value())) {\n if (config?.error) {\n return getOption(config.error, ctx);\n } else {\n return emailError({message: getOption(config?.message, ctx)});\n }\n }\n\n return undefined;\n });\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {LogicFn, PathKind, SchemaPath, SchemaPathRules} from '../../types';\nimport {createMetadataKey, MAX, metadata} from '../metadata';\nimport {BaseValidatorConfig, getOption, isEmpty} from './util';\nimport {validate} from './validate';\nimport {maxError} from './validation_errors';\n\n/**\n * Binds a validator to the given path that requires the value to be less than or equal to the\n * given `maxValue`.\n * This function can only be called on number paths.\n * In addition to binding a validator, this function adds `MAX` property to the field.\n *\n * @param path Path of the field to validate\n * @param maxValue The maximum value, or a LogicFn that returns the maximum value.\n * @param config Optional, allows providing any of the following options:\n * - `error`: Custom validation error(s) to be used instead of the default `ValidationError.max(maxValue)`\n * or a function that receives the `FieldContext` and returns custom validation error(s).\n * @template TPathKind The kind of path the logic is bound to (a root path, child path, or item of an array)\n *\n * @category validation\n * @experimental 21.0.0\n */\nexport function max<TPathKind extends PathKind = PathKind.Root>(\n path: SchemaPath<number | string | null, SchemaPathRules.Supported, TPathKind>,\n maxValue: number | LogicFn<number | string | null, number | undefined, TPathKind>,\n config?: BaseValidatorConfig<number | string | null, TPathKind>,\n) {\n const MAX_MEMO = metadata(path, createMetadataKey<number | undefined>(), (ctx) =>\n typeof maxValue === 'number' ? maxValue : maxValue(ctx),\n );\n metadata(path, MAX, ({state}) => state.metadata(MAX_MEMO)!());\n validate(path, (ctx) => {\n if (isEmpty(ctx.value())) {\n return undefined;\n }\n const max = ctx.state.metadata(MAX_MEMO)!();\n if (max === undefined || Number.isNaN(max)) {\n return undefined;\n }\n const value = ctx.value();\n const numValue = !value && value !== 0 ? NaN : Number(value); // Treat `''` and `null` as `NaN`\n if (numValue > max) {\n if (config?.error) {\n return getOption(config.error, ctx);\n } else {\n return maxError(max, {message: getOption(config?.message, ctx)});\n }\n }\n return undefined;\n });\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {LogicFn, PathKind, SchemaPath, SchemaPathRules} from '../../types';\nimport {createMetadataKey, MAX_LENGTH, metadata} from '../metadata';\nimport {\n BaseValidatorConfig,\n getLengthOrSize,\n getOption,\n isEmpty,\n ValueWithLengthOrSize,\n} from './util';\nimport {validate} from './validate';\nimport {maxLengthError} from './validation_errors';\n\n/**\n * Binds a validator to the given path that requires the length of the value to be less than or\n * equal to the given `maxLength`.\n * This function can only be called on string or array paths.\n * In addition to binding a validator, this function adds `MAX_LENGTH` property to the field.\n *\n * @param path Path of the field to validate\n * @param maxLength The maximum length, or a LogicFn that returns the maximum length.\n * @param config Optional, allows providing any of the following options:\n * - `error`: Custom validation error(s) to be used instead of the default `ValidationError.maxLength(maxLength)`\n * or a function that receives the `FieldContext` and returns custom validation error(s).\n * @template TValue The type of value stored in the field the logic is bound to.\n * @template TPathKind The kind of path the logic is bound to (a root path, child path, or item of an array)\n *\n * @category validation\n * @experimental 21.0.0\n */\nexport function maxLength<\n TValue extends ValueWithLengthOrSize,\n TPathKind extends PathKind = PathKind.Root,\n>(\n path: SchemaPath<TValue, SchemaPathRules.Supported, TPathKind>,\n maxLength: number | LogicFn<TValue, number | undefined, TPathKind>,\n config?: BaseValidatorConfig<TValue, TPathKind>,\n) {\n const MAX_LENGTH_MEMO = metadata(path, createMetadataKey<number | undefined>(), (ctx) =>\n typeof maxLength === 'number' ? maxLength : maxLength(ctx),\n );\n metadata(path, MAX_LENGTH, ({state}) => state.metadata(MAX_LENGTH_MEMO)!());\n validate(path, (ctx) => {\n if (isEmpty(ctx.value())) {\n return undefined;\n }\n const maxLength = ctx.state.metadata(MAX_LENGTH_MEMO)!();\n if (maxLength === undefined) {\n return undefined;\n }\n if (getLengthOrSize(ctx.value()) > maxLength) {\n if (config?.error) {\n return getOption(config.error, ctx);\n } else {\n return maxLengthError(maxLength, {message: getOption(config?.message, ctx)});\n }\n }\n return undefined;\n });\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {LogicFn, PathKind, SchemaPath, SchemaPathRules} from '../../types';\nimport {createMetadataKey, metadata, MIN} from '../metadata';\nimport {BaseValidatorConfig, getOption, isEmpty} from './util';\nimport {validate} from './validate';\nimport {minError} from './validation_errors';\n\n/**\n * Binds a validator to the given path that requires the value to be greater than or equal to\n * the given `minValue`.\n * This function can only be called on number paths.\n * In addition to binding a validator, this function adds `MIN` property to the field.\n *\n * @param path Path of the field to validate\n * @param minValue The minimum value, or a LogicFn that returns the minimum value.\n * @param config Optional, allows providing any of the following options:\n * - `error`: Custom validation error(s) to be used instead of the default `ValidationError.min(minValue)`\n * or a function that receives the `FieldContext` and returns custom validation error(s).\n * @template TPathKind The kind of path the logic is bound to (a root path, child path, or item of an array)\n *\n * @category validation\n * @experimental 21.0.0\n */\nexport function min<\n TValue extends number | string | null,\n TPathKind extends PathKind = PathKind.Root,\n>(\n path: SchemaPath<TValue, SchemaPathRules.Supported, TPathKind>,\n minValue: number | LogicFn<TValue, number | undefined, TPathKind>,\n config?: BaseValidatorConfig<TValue, TPathKind>,\n) {\n const MIN_MEMO = metadata(path, createMetadataKey<number | undefined>(), (ctx) =>\n typeof minValue === 'number' ? minValue : minValue(ctx),\n );\n metadata(path, MIN, ({state}) => state.metadata(MIN_MEMO)!());\n validate(path, (ctx) => {\n if (isEmpty(ctx.value())) {\n return undefined;\n }\n const min = ctx.state.metadata(MIN_MEMO)!();\n if (min === undefined || Number.isNaN(min)) {\n return undefined;\n }\n const value = ctx.value();\n const numValue = !value && value !== 0 ? NaN : Number(value); // Treat `''` and `null` as `NaN`\n if (numValue < min) {\n if (config?.error) {\n return getOption(config.error, ctx);\n } else {\n return minError(min, {message: getOption(config?.message, ctx)});\n }\n }\n return undefined;\n });\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {LogicFn, PathKind, SchemaPath, SchemaPathRules} from '../../types';\nimport {createMetadataKey, metadata, MIN_LENGTH} from '../metadata';\nimport {\n BaseValidatorConfig,\n getLengthOrSize,\n getOption,\n isEmpty,\n ValueWithLengthOrSize,\n} from './util';\nimport {validate} from './validate';\nimport {minLengthError} from './validation_errors';\n\n/**\n * Binds a validator to the given path that requires the length of the value to be greater than or\n * equal to the given `minLength`.\n * This function can only be called on string or array paths.\n * In addition to binding a validator, this function adds `MIN_LENGTH` property to