@gravionlabs/helix-zod 22.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,457 @@
1
+ import * as _angular_core from '@angular/core';
2
+ import { WritableSignal, InjectionToken, Type, EnvironmentProviders } from '@angular/core';
3
+ import * as _angular_forms_signals from '@angular/forms/signals';
4
+ import { SchemaPath, Field, ValidationError, FieldTree, SchemaFn, FieldState, FormValueControl } from '@angular/forms/signals';
5
+ import { z, ZodSchema } from 'zod';
6
+ import { ValidatorKey } from '@gravionlabs/helix-core/validators';
7
+ import * as _gravionlabs_helix_zod from '@gravionlabs/helix-zod';
8
+ import { ValidatorFn } from '@angular/forms';
9
+
10
+ /**
11
+ * Built-in widget kinds plus any custom key registered via
12
+ * `provideHelixDynamicForms({ widgets })`.
13
+ */
14
+ type HelixWidgetKind = 'text' | 'email' | 'password' | 'textarea' | 'number' | 'checkbox' | 'select' | 'date' | 'object' | 'array' | 'union' | (string & {});
15
+ interface HelixSelectOption {
16
+ label: string;
17
+ value: unknown;
18
+ }
19
+ /**
20
+ * UI metadata attached to a Zod field schema. Drives labels, widget selection,
21
+ * ordering and the conditional engine of the dynamic form renderer.
22
+ *
23
+ * Attach it with {@link helixMeta} (preferred — keeps the same schema instance)
24
+ * or via `schema.meta({ title, description, helix: { ... } })`.
25
+ *
26
+ * @template TRoot The root form model type the conditional predicates receive.
27
+ */
28
+ interface HelixFieldMeta<TRoot = Record<string, unknown>> {
29
+ label?: string;
30
+ placeholder?: string;
31
+ hint?: string;
32
+ /** Overrides the widget inferred from the Zod type. */
33
+ widget?: HelixWidgetKind;
34
+ /** Options for select-like widgets. Inferred from `z.enum()` when omitted. */
35
+ options?: readonly HelixSelectOption[] | readonly string[];
36
+ /** Render order among siblings — lower first; fields without order keep shape order. */
37
+ order?: number;
38
+ /** Label of the add button for array widgets. */
39
+ addLabel?: string;
40
+ /** Label of the remove button for array widgets. */
41
+ removeLabel?: string;
42
+ /** Hides the field (and excludes its errors from parent validity) when true. */
43
+ hiddenWhen?: (root: TRoot) => boolean;
44
+ /** Disables the field when truthy; a string return becomes the disabled reason. */
45
+ disabledWhen?: (root: TRoot) => boolean | string;
46
+ readonlyWhen?: (root: TRoot) => boolean;
47
+ /** Marks the field required (signal-forms `required()` with `when`). */
48
+ requiredWhen?: (root: TRoot) => boolean;
49
+ /**
50
+ * Escape hatch: additional signal-forms rules applied to this field's path
51
+ * inside the generated schema (e.g. `p => validate(p, ...)`).
52
+ */
53
+ extraSchema?: (path: SchemaPath<any>) => void;
54
+ /** Arbitrary passthrough for custom widgets. */
55
+ [key: string]: unknown;
56
+ }
57
+ /**
58
+ * Typed registry backing {@link helixMeta}. Checked before the zod global
59
+ * registry by {@link readHelixMeta}.
60
+ */
61
+ declare const helixFieldMetaRegistry: z.core.$ZodRegistry<HelixFieldMeta<Record<string, unknown>>, z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
62
+ /**
63
+ * Attaches Helix UI metadata to a Zod schema and returns the **same** instance.
64
+ *
65
+ * Prefer this over `.meta()`, which clones the schema — metadata must live on
66
+ * the exact instance composed into the `z.object()` shape for the walker to
67
+ * find it.
68
+ *
69
+ * @example
70
+ * const UserSchema = z.object({
71
+ * email: helixMeta(z.email(), { label: 'E-mail', placeholder: 'you@example.com' }),
72
+ * });
73
+ */
74
+ declare function helixMeta<S extends z.ZodType, TRoot = Record<string, unknown>>(schema: S, meta: HelixFieldMeta<TRoot>): S;
75
+ /**
76
+ * Reads {@link HelixFieldMeta} from a Zod schema: the typed registry first,
77
+ * then the zod global registry (`.meta()` — `title` → `label`,
78
+ * `description` → `hint`, `helix` key merged on top).
79
+ *
80
+ * Wrapper types (`optional`, `nullable`, `default`, `readonly`) are unwrapped
81
+ * and their metadata merged — outermost wins.
82
+ */
83
+ declare function readHelixMeta(schema: z.ZodType): HelixFieldMeta;
84
+
85
+ /**
86
+ * Renderer-facing description of one form field, produced by
87
+ * `zodToFieldDescriptors` from an annotated Zod schema.
88
+ */
89
+ interface HelixFieldDescriptor {
90
+ /** Property name in the parent shape; `''` for the root descriptor. */
91
+ key: string;
92
+ /** Path from the root model to this field. */
93
+ path: readonly string[];
94
+ /** Resolved widget: `meta.widget` override or the kind inferred from the Zod type. */
95
+ widget: HelixWidgetKind;
96
+ meta: HelixFieldMeta;
97
+ /** The unwrapped inner Zod type (optional/nullable/default wrappers peeled). */
98
+ zodType: z.ZodType;
99
+ /** The original Zod schema including wrappers — used to build default values. */
100
+ zodSource: z.ZodType;
101
+ /** True when the field is neither optional nor nullable and has no default. */
102
+ required: boolean;
103
+ /** Native `<input type>` for input-based widgets. */
104
+ inputType?: string;
105
+ /** Options for select-like widgets (from meta or `z.enum()`). */
106
+ options?: readonly HelixSelectOption[];
107
+ /** Child descriptors for object fields, sorted by `meta.order`. */
108
+ children?: readonly HelixFieldDescriptor[];
109
+ /** Element descriptor for array fields (path relative to the array item). */
110
+ itemDescriptor?: HelixFieldDescriptor;
111
+ /** Variant map for discriminated unions. */
112
+ union?: {
113
+ discriminator: string;
114
+ /** Discriminator literal → object descriptor of that variant. */
115
+ variants: ReadonlyMap<string | number | boolean, HelixFieldDescriptor>;
116
+ };
117
+ }
118
+
119
+ /**
120
+ * Dispatches one field descriptor to its registered widget component.
121
+ *
122
+ * Handles `hidden()` centrally: a hidden field renders nothing, per the
123
+ * signal-forms guidance to `@if` on the hidden state.
124
+ *
125
+ * Imports no widget statically — composite widgets (object/array/union) import
126
+ * this component, and built-in registrations live in
127
+ * `provideHelixDynamicForms()`, which breaks the import cycle.
128
+ */
129
+ declare class HelixDynamicField {
130
+ #private;
131
+ readonly field: _angular_core.InputSignal<Field<any>>;
132
+ readonly descriptor: _angular_core.InputSignal<HelixFieldDescriptor>;
133
+ protected readonly component: _angular_core.Signal<_angular_core.Type<unknown>>;
134
+ protected readonly outletInputs: _angular_core.Signal<{
135
+ field: Field<any>;
136
+ descriptor: HelixFieldDescriptor;
137
+ }>;
138
+ protected readonly hiddenState: _angular_core.Signal<boolean>;
139
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<HelixDynamicField, never>;
140
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<HelixDynamicField, "helix-dynamic-field", never, { "field": { "alias": "field"; "required": true; "isSignal": true; }; "descriptor": { "alias": "descriptor"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
141
+ }
142
+
143
+ /**
144
+ * Renders a complete signal form from an annotated Zod object schema.
145
+ *
146
+ * - Validation: root-level `validateStandardSchema` — Zod issues are routed to
147
+ * the matching fields.
148
+ * - Model: pass your own `WritableSignal` via `model`, or let the component
149
+ * derive an initial value from the schema (`buildDefaultValue`).
150
+ * - Swapping the `schema` input recreates the form and resets its state.
151
+ * - `submitted` emits the **parsed** (`schema.parse`) value on valid submit.
152
+ *
153
+ * Requires `provideHelixDynamicForms()` in the injector chain.
154
+ */
155
+ declare class HelixDynamicForm<T extends Record<string, unknown> = Record<string, unknown>> {
156
+ #private;
157
+ readonly schema: _angular_core.InputSignal<z.ZodObject<z.core.$ZodLooseShape, z.core.$strip>>;
158
+ /** Optional external model — omit to derive the initial value from the schema. */
159
+ readonly model: _angular_core.InputSignal<WritableSignal<T> | undefined>;
160
+ readonly submitLabel: _angular_core.InputSignal<string>;
161
+ readonly showSubmit: _angular_core.InputSignal<boolean>;
162
+ readonly submitted: _angular_core.OutputEmitterRef<T>;
163
+ protected readonly descriptors: _angular_core.Signal<HelixFieldDescriptor>;
164
+ /** The form's `FieldTree` — recreated (state reset) when the schema changes. */
165
+ readonly formTree: _angular_core.Signal<_angular_forms_signals.FieldTree<T, string | number, "writable">>;
166
+ protected childField(descriptor: HelixFieldDescriptor): any;
167
+ protected handleSubmit(event: Event): Promise<void>;
168
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<HelixDynamicForm<any>, never>;
169
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<HelixDynamicForm<any>, "helix-dynamic-form", never, { "schema": { "alias": "schema"; "required": true; "isSignal": true; }; "model": { "alias": "model"; "required": false; "isSignal": true; }; "submitLabel": { "alias": "submitLabel"; "required": false; "isSignal": true; }; "showSubmit": { "alias": "showSubmit"; "required": false; "isSignal": true; }; }, { "submitted": "submitted"; }, never, never, true, never>;
170
+ }
171
+
172
+ /**
173
+ * App-level hook to customize user-facing error messages centrally.
174
+ * Return a string to use it, or `null`/`undefined` to fall back to the
175
+ * default message for the error.
176
+ */
177
+ type HelixErrorMessageResolver = (error: ValidationError, helixKey: ValidatorKey | null) => string | null | undefined;
178
+ /**
179
+ * Maps a signal-forms `ValidationError` to the `ValidatorKey` message
180
+ * convention shared with `HelixZodValidators.fromZod`.
181
+ *
182
+ * @param error The signal-forms validation error.
183
+ * @param value The current field value — used to distinguish Required from
184
+ * type errors on Zod `invalid_type` issues (Zod v4 dropped the
185
+ * `received` field).
186
+ */
187
+ declare function helixErrorKey(error: ValidationError, value?: unknown): ValidatorKey | null;
188
+ /**
189
+ * Returns the message of the first displayable error, mirroring
190
+ * `HelixFormField`'s first-error convention.
191
+ *
192
+ * Order of precedence per error: `resolver` result → Zod issue message /
193
+ * signal-forms error message. Errors yielding no message are skipped.
194
+ */
195
+ declare function helixFirstErrorMessage(errors: readonly ValidationError[], options?: {
196
+ value?: unknown;
197
+ resolver?: HelixErrorMessageResolver;
198
+ }): string | null;
199
+
200
+ /** Registers a widget component for a {@link HelixWidgetKind}. */
201
+ interface HelixFieldWidgetRegistration {
202
+ widget: HelixWidgetKind;
203
+ /** Component with `field` and `descriptor` inputs (see `HelixFieldWidgetBase`). */
204
+ component: Type<unknown>;
205
+ }
206
+ /**
207
+ * Multi-provider token holding widget registrations. Later registrations win,
208
+ * so consumer widgets registered after the built-ins override them.
209
+ */
210
+ declare const HELIX_DYNAMIC_FIELD_WIDGETS: InjectionToken<readonly HelixFieldWidgetRegistration[][]>;
211
+ /** Optional app-level error message resolver used by all built-in widgets. */
212
+ declare const HELIX_ERROR_MESSAGE_RESOLVER: InjectionToken<HelixErrorMessageResolver>;
213
+ /** Resolves the widget component registered for a widget kind. */
214
+ declare class HelixFieldWidgetResolver {
215
+ #private;
216
+ resolve(widget: HelixWidgetKind): Type<unknown>;
217
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<HelixFieldWidgetResolver, never>;
218
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<any>;
219
+ }
220
+
221
+ interface HelixDynamicFormsConfig {
222
+ /**
223
+ * Additional widget registrations. Registered after the built-ins, so a
224
+ * registration for an existing kind (e.g. `'select'`) overrides it.
225
+ */
226
+ widgets?: readonly HelixFieldWidgetRegistration[];
227
+ /** Central override hook for user-facing error messages. */
228
+ errorMessageResolver?: HelixErrorMessageResolver;
229
+ }
230
+ /**
231
+ * Provides the dynamic-form widget registry (built-in widgets included) and
232
+ * optional configuration. Add it to `ApplicationConfig.providers` or a route's
233
+ * `providers`.
234
+ */
235
+ declare function provideHelixDynamicForms(config?: HelixDynamicFormsConfig): EnvironmentProviders;
236
+
237
+ /**
238
+ * Navigates a `FieldTree` by a root-relative property path
239
+ * (e.g. `['address', 'city']`).
240
+ */
241
+ declare function fieldAtPath(tree: FieldTree<any>, path: readonly string[]): any;
242
+
243
+ /**
244
+ * Builds the signal-forms schema for a Zod-driven dynamic form:
245
+ *
246
+ * 1. Root-level `validateStandardSchema(rootPath, zodSchema)` — Zod issue
247
+ * paths route errors onto the matching child fields, including cross-field
248
+ * `.refine()` errors with a `path`.
249
+ * 2. Conditional field state from {@link HelixFieldMeta} predicates
250
+ * (`hiddenWhen`/`disabledWhen`/`readonlyWhen`/`requiredWhen`), evaluated
251
+ * against the root form value.
252
+ * 3. `meta.extraSchema` escape-hatch rules per field.
253
+ * 4. `applyEach` for array items and `applyWhenValue` for discriminated-union
254
+ * variants, so variant/item rules stay dormant unless active.
255
+ */
256
+ declare function buildHelixSchema<T extends Record<string, unknown>>(zodSchema: z.ZodObject, root: HelixFieldDescriptor): SchemaFn<T>;
257
+
258
+ /**
259
+ * Builds a fully-populated initial model value for a Zod schema — signal forms
260
+ * require a concrete model shape up front.
261
+ *
262
+ * Rules: `.default()` wins; strings → `''`; numbers/dates → `null` (paired
263
+ * with parse-transforming widgets); booleans → `false`; enums → first option;
264
+ * arrays → `[]`; objects → recursed shape; discriminated unions → default of
265
+ * the first variant; `optional`/`nullable` leaves without a more specific
266
+ * default → `undefined` / `null`.
267
+ */
268
+ declare function buildDefaultValue(schema: z.ZodType): unknown;
269
+
270
+ /**
271
+ * Walks an annotated `z.object()` schema and produces the recursive
272
+ * {@link HelixFieldDescriptor} tree consumed by the dynamic form renderer.
273
+ *
274
+ * Widget inference: string→text (`z.email()`→email), number→number,
275
+ * boolean→checkbox, date→date, enum/literal→select, array→array,
276
+ * object→object, discriminated union→union. Anything else requires a
277
+ * `widget` override in {@link HelixFieldMeta} (throws in dev mode otherwise).
278
+ */
279
+ declare function zodToFieldDescriptors(schema: z.ZodObject): HelixFieldDescriptor;
280
+
281
+ /**
282
+ * Base class for dynamic-form widgets. A widget is a standalone component with
283
+ * the two inputs below; `HelixDynamicField` instantiates it via
284
+ * `NgComponentOutlet` and feeds them in.
285
+ */
286
+ declare abstract class HelixFieldWidgetBase<T = any> {
287
+ #private;
288
+ readonly field: _angular_core.InputSignal<Field<T>>;
289
+ readonly descriptor: _angular_core.InputSignal<HelixFieldDescriptor>;
290
+ protected readonly state: _angular_core.Signal<FieldState<T, string | number>>;
291
+ /** Touched-gated first error message — mirrors `HelixFormField.activeError`. */
292
+ protected readonly firstError: _angular_core.Signal<string | null>;
293
+ protected readonly label: _angular_core.Signal<string>;
294
+ protected readonly hint: _angular_core.Signal<string | undefined>;
295
+ protected readonly placeholder: _angular_core.Signal<string>;
296
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<HelixFieldWidgetBase<any>, never>;
297
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<HelixFieldWidgetBase<any>, never, never, { "field": { "alias": "field"; "required": true; "isSignal": true; }; "descriptor": { "alias": "descriptor"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
298
+ }
299
+
300
+ /** Built-in widget for `z.array()` fields — item list with add/remove. */
301
+ declare class HelixArrayWidget extends HelixFieldWidgetBase<unknown[]> {
302
+ protected readonly itemFields: _angular_core.Signal<any[]>;
303
+ protected readonly addLabel: _angular_core.Signal<string>;
304
+ protected readonly removeLabel: _angular_core.Signal<string>;
305
+ protected add(): void;
306
+ protected removeAt(index: number): void;
307
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<HelixArrayWidget, never>;
308
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<HelixArrayWidget, "helix-array-widget", never, {}, {}, never, never, true, never>;
309
+ }
310
+
311
+ /** Built-in widget for boolean fields — checkbox with inline label. */
312
+ declare class HelixCheckboxWidget extends HelixFieldWidgetBase<boolean> {
313
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<HelixCheckboxWidget, never>;
314
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<HelixCheckboxWidget, "helix-checkbox-widget", never, {}, {}, never, never, true, never>;
315
+ }
316
+
317
+ /**
318
+ * Native `<input type="date">` exposed as a signal-forms `FormValueControl`
319
+ * with a `Date | null` model — string↔Date conversion via `transformedValue`.
320
+ */
321
+ declare class HelixDateInput implements FormValueControl<Date | null> {
322
+ readonly value: _angular_core.ModelSignal<Date | null>;
323
+ readonly disabled: _angular_core.InputSignal<boolean>;
324
+ readonly readonly: _angular_core.InputSignal<boolean>;
325
+ readonly touched: _angular_core.ModelSignal<boolean>;
326
+ protected readonly rawValue: _angular_forms_signals.TransformedValueSignal<string>;
327
+ protected onInput(event: Event): void;
328
+ protected onBlur(): void;
329
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<HelixDateInput, never>;
330
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<HelixDateInput, "helix-date-input", never, { "value": { "alias": "value"; "required": true; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "readonly": { "alias": "readonly"; "required": false; "isSignal": true; }; "touched": { "alias": "touched"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "touched": "touchedChange"; }, never, never, true, never>;
331
+ }
332
+
333
+ /** Built-in widget for `z.date()` fields. */
334
+ declare class HelixDateWidget extends HelixFieldWidgetBase<Date | null> {
335
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<HelixDateWidget, never>;
336
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<HelixDateWidget, "helix-date-widget", never, {}, {}, never, never, true, never>;
337
+ }
338
+
339
+ /** Built-in widget for numeric fields. */
340
+ declare class HelixNumberWidget extends HelixFieldWidgetBase<number | null> {
341
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<HelixNumberWidget, never>;
342
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<HelixNumberWidget, "helix-number-widget", never, {}, {}, never, never, true, never>;
343
+ }
344
+
345
+ /** Built-in widget for nested `z.object()` fields — fieldset + recursion. */
346
+ declare class HelixObjectWidget extends HelixFieldWidgetBase<Record<string, unknown>> {
347
+ protected childField(key: string): any;
348
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<HelixObjectWidget, never>;
349
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<HelixObjectWidget, "helix-object-widget", never, {}, {}, never, never, true, never>;
350
+ }
351
+
352
+ /**
353
+ * Built-in widget for enum/select fields — wraps `HelixSelect`, which the
354
+ * signal-forms `FormField` directive binds via its `ControlValueAccessor`.
355
+ */
356
+ declare class HelixSelectWidget extends HelixFieldWidgetBase<unknown> {
357
+ protected readonly selectOptions: _angular_core.Signal<_gravionlabs_helix_zod.HelixSelectOption[]>;
358
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<HelixSelectWidget, never>;
359
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<HelixSelectWidget, "helix-select-widget", never, {}, {}, never, never, true, never>;
360
+ }
361
+
362
+ /** Built-in widget for `text`, `email` and `password` fields. */
363
+ declare class HelixTextWidget extends HelixFieldWidgetBase<string> {
364
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<HelixTextWidget, never>;
365
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<HelixTextWidget, "helix-text-widget", never, {}, {}, never, never, true, never>;
366
+ }
367
+
368
+ /** Built-in widget for multiline text (`widget: 'textarea'`). */
369
+ declare class HelixTextareaWidget extends HelixFieldWidgetBase<string> {
370
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<HelixTextareaWidget, never>;
371
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<HelixTextareaWidget, "helix-textarea-widget", never, {}, {}, never, never, true, never>;
372
+ }
373
+
374
+ /**
375
+ * Built-in widget for `z.discriminatedUnion()` fields: a discriminator select
376
+ * plus the active variant's fields.
377
+ *
378
+ * Switching the discriminator **resets** the union value to the new variant's
379
+ * default (preserving the discriminator) — mandatory so the model always
380
+ * matches the active variant's shape and all field paths exist.
381
+ */
382
+ declare class HelixUnionWidget extends HelixFieldWidgetBase<Record<string, unknown>> {
383
+ protected readonly discriminator: _angular_core.Signal<string>;
384
+ protected readonly tags: _angular_core.Signal<(string | number | boolean)[]>;
385
+ protected readonly activeTag: _angular_core.Signal<unknown>;
386
+ protected readonly variantChildren: _angular_core.Signal<_gravionlabs_helix_zod.HelixFieldDescriptor[]>;
387
+ protected childField(key: string): any;
388
+ protected onTagChange(event: Event): void;
389
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<HelixUnionWidget, never>;
390
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<HelixUnionWidget, "helix-union-widget", never, {}, {}, never, never, true, never>;
391
+ }
392
+
393
+ interface ZodHelixOptions {
394
+ /**
395
+ * Required when the schema contains `.refine()` or `.superRefine()`.
396
+ * Those produce `ZodIssueCode.custom` which has no automatic `ValidatorKey`
397
+ * mapping. The bridge throws a descriptive error in development (`ngDevMode`)
398
+ * and skips silently in production when this is missing and a custom issue occurs.
399
+ *
400
+ * @example
401
+ * HelixZodValidators.fromZod(
402
+ * z.string().refine(v => !banned.includes(v), 'Not allowed'),
403
+ * { fallbackKey: ValidatorKey.Pattern },
404
+ * )
405
+ */
406
+ fallbackKey?: ValidatorKey;
407
+ /**
408
+ * When `true` (default), empty values (`''`, `null`, `undefined`) bypass Zod
409
+ * and return `null` — matching `Validators`' default `allowEmpty = true`.
410
+ * Set to `false` to let Zod validate empty values (e.g. for required fields).
411
+ */
412
+ allowEmpty?: boolean;
413
+ }
414
+ declare const HelixZodValidators: {
415
+ /**
416
+ * Converts a Zod field schema into a Helix-compatible Angular `ValidatorFn`.
417
+ *
418
+ * Each `ZodIssue` is mapped to its `ValidatorKey`. `HelixFormField` reads
419
+ * those keys directly — no template changes required.
420
+ *
421
+ * All issues from a single `safeParse` are processed simultaneously, producing
422
+ * one `ValidationErrors` key per issue — equivalent to stacking multiple
423
+ * `Validators` calls.
424
+ *
425
+ * ### Known gaps
426
+ * - `ValidatorKey.OneOf` / `AllOf` — `z.enum()` produces `invalid_value`,
427
+ * which has no automatic mapping. Use `fallbackKey` or continue using
428
+ * `Validators.oneOf` / `Validators.allOf`.
429
+ * - `invalid_type` for `boolean` expected — Helix has no `Boolean` key.
430
+ * Use `fallbackKey` or `Validators.pattern`.
431
+ *
432
+ * @param schema A Zod field schema (e.g. `UserSchema.shape.email`).
433
+ * Do NOT pass schemas with `.transform()` — use the
434
+ * input-only (pre-transform) variant for form controls.
435
+ * @param options `allowEmpty` (default `true`) and optional `fallbackKey`
436
+ * for `.refine()` / `custom` validators.
437
+ *
438
+ * @example
439
+ * // Standard field — no fallbackKey needed
440
+ * HelixZodValidators.fromZod(UserSchema.shape.email)
441
+ *
442
+ * @example
443
+ * // Schema with .refine() — fallbackKey required (Option B)
444
+ * HelixZodValidators.fromZod(
445
+ * z.string().refine(v => !banned.includes(v), 'Username not allowed'),
446
+ * { fallbackKey: ValidatorKey.Pattern },
447
+ * )
448
+ *
449
+ * @example
450
+ * // Required field — disable allowEmpty so empty value triggers Required / MinLength
451
+ * HelixZodValidators.fromZod(UserSchema.shape.name, { allowEmpty: false })
452
+ */
453
+ fromZod(schema: ZodSchema, options?: ZodHelixOptions): ValidatorFn;
454
+ };
455
+
456
+ export { HELIX_DYNAMIC_FIELD_WIDGETS, HELIX_ERROR_MESSAGE_RESOLVER, HelixArrayWidget, HelixCheckboxWidget, HelixDateInput, HelixDateWidget, HelixDynamicField, HelixDynamicForm, HelixFieldWidgetBase, HelixFieldWidgetResolver, HelixNumberWidget, HelixObjectWidget, HelixSelectWidget, HelixTextWidget, HelixTextareaWidget, HelixUnionWidget, HelixZodValidators, buildDefaultValue, buildHelixSchema, fieldAtPath, helixErrorKey, helixFieldMetaRegistry, helixFirstErrorMessage, helixMeta, provideHelixDynamicForms, readHelixMeta, zodToFieldDescriptors };
457
+ export type { HelixDynamicFormsConfig, HelixErrorMessageResolver, HelixFieldDescriptor, HelixFieldMeta, HelixFieldWidgetRegistration, HelixSelectOption, HelixWidgetKind, ZodHelixOptions };