@uniform-ts/core 0.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,857 @@
1
+ import * as React from 'react';
2
+ import { FieldValues, FieldPath, FieldPathValue, RefCallBack, Control } from 'react-hook-form';
3
+ import * as z from 'zod/v4/core';
4
+ import * as react_jsx_runtime from 'react/jsx-runtime';
5
+
6
+ /**
7
+ * Extracts the element type of an array type.
8
+ * e.g. `ArrayItem<{ name: string }[]>` → `{ name: string }`
9
+ */
10
+ type ArrayItem<T> = T extends (infer U)[] ? U : never;
11
+ /**
12
+ * Recursively produces all valid `fields` prop keys for a given schema shape:
13
+ *
14
+ * - Scalar fields → just their key (e.g. `"name"`)
15
+ * - Object fields → their key + all dot-notated child paths
16
+ * (e.g. `"address"` | `"address.street"`)
17
+ * - Array fields → their key + the unprefixed keys of the item object, so
18
+ * you can target every row's sub-field uniformly
19
+ * (e.g. `"items"` | `"items.name"` | `"items.qty"`)
20
+ * Index-based paths like `"items.0.name"` are intentionally
21
+ * excluded — row count is dynamic at runtime.
22
+ *
23
+ * @example
24
+ * // Given { name: string; address: { street: string }; items: { qty: number }[] }
25
+ * // DeepKeys produces:
26
+ * // "name" | "address" | "address.street" | "items" | "items.qty"
27
+ */
28
+ type DeepKeys<T> = T extends object ? {
29
+ [K in keyof T & string]: T[K] extends unknown[] ? ArrayItem<T[K]> extends object ? K | `${K}.${DeepKeys<ArrayItem<T[K]>>}` : K : T[K] extends object ? K | `${K}.${DeepKeys<T[K]>}` : K;
30
+ }[keyof T & string] : never;
31
+ /**
32
+ * Resolves the value type at a dot-notated path within a type `T`.
33
+ * Array fields use the unprefixed child path (matching `DeepKeys` convention).
34
+ *
35
+ * @example
36
+ * // DeepFieldValue<{ name: string; items: { qty: number }[] }, 'items.qty'> → number
37
+ */
38
+ type DeepFieldValue<T, K extends string> = K extends keyof T ? T[K] : K extends `${infer Head}.${infer Tail}` ? Head extends keyof T ? T[Head] extends (infer Item)[] ? DeepFieldValue<NonNullable<Item>, Tail> : DeepFieldValue<NonNullable<T[Head]>, Tail> : unknown : unknown;
39
+ /**
40
+ * The resolved primitive or structural type of a schema field, as determined
41
+ * by introspecting the Zod schema. Used internally to decide which field
42
+ * component to render.
43
+ */
44
+ type FieldType = 'string' | 'number' | 'boolean' | 'date' | 'select' | 'object' | 'array' | 'union' | 'unknown';
45
+ /**
46
+ * A single option entry used in `select` / enum fields.
47
+ */
48
+ type SelectOption = {
49
+ /** Human-readable text displayed in the dropdown. */
50
+ label: string;
51
+ /** The underlying value submitted with the form. */
52
+ value: string | number;
53
+ };
54
+ /**
55
+ * A predicate function that receives the current form values and returns
56
+ * `true` when the field should be visible, `false` when it should be hidden.
57
+ *
58
+ * @template TValues - The shape of the form values object.
59
+ */
60
+ type FieldCondition<TValues = Record<string, unknown>> = (values: TValues) => boolean;
61
+ /**
62
+ * All programmatic form control methods, shared by both the field `onChange`
63
+ * callback and the imperative ref handle.
64
+ *
65
+ * @template TSchema - The Zod object schema that defines the form shape.
66
+ */
67
+ type FormMethods<TValues extends FieldValues = FieldValues> = {
68
+ /** Set a single field value programmatically */
69
+ setValue: <K extends FieldPath<TValues>>(name: K, value: FieldPathValue<TValues, K>) => void;
70
+ /** Set multiple field values at once */
71
+ setValues: (values: Partial<TValues>) => void;
72
+ /** Get the current form values */
73
+ getValues: () => TValues;
74
+ /** Reset a single field to its default value */
75
+ resetField: (name: FieldPath<TValues>) => void;
76
+ /** Reset the entire form, optionally to new values */
77
+ reset: (values?: Partial<TValues>) => void;
78
+ /** Set a validation error on a specific field */
79
+ setError: (name: FieldPath<TValues>, message: string) => void;
80
+ /** Set validation errors on multiple fields at once */
81
+ setErrors: (errors: Partial<Record<FieldPath<TValues>, string>>) => void;
82
+ /** Clear validation errors (all fields, or specific ones) */
83
+ clearErrors: (names?: FieldPath<TValues> | FieldPath<TValues>[]) => void;
84
+ /** Programmatically trigger form submission */
85
+ submit: () => void;
86
+ /** Focus a specific field by name (dot-notated for nested fields) */
87
+ focus: (fieldName: FieldPath<TValues>) => void;
88
+ /** Get the current value of a field (or all values if no name provided) */
89
+ watch: {
90
+ (): TValues;
91
+ <K extends FieldPath<TValues>>(name: K): FieldPathValue<TValues, K>;
92
+ };
93
+ };
94
+ /**
95
+ * Dynamic field property overrides passed to `ctx.setFieldMeta()` inside a
96
+ * UniForm onChange handler. Each key is optional — only the properties you
97
+ * provide will be applied; omitted keys leave the current field state unchanged.
98
+ */
99
+ type FieldDependencyResult = {
100
+ /** Override the available options for select fields */
101
+ options?: SelectOption[];
102
+ /** Dynamically show or hide the field */
103
+ hidden?: boolean;
104
+ /** Dynamically enable or disable the field */
105
+ disabled?: boolean;
106
+ /** Override the field label */
107
+ label?: string;
108
+ /** Override the placeholder text */
109
+ placeholder?: string;
110
+ /** Override the description text */
111
+ description?: string;
112
+ };
113
+ /**
114
+ * The base set of per-field UI metadata that can be provided via the `fields`
115
+ * prop or through Zod schema extensions (`.meta()`).
116
+ *
117
+ * `FieldMeta` extends this type with an index signature to allow arbitrary
118
+ * extra keys for custom component use-cases.
119
+ */
120
+ type FieldMetaBase = {
121
+ /** Human-readable label rendered above the field. Falls back to a derived label from the field name. */
122
+ label?: string;
123
+ /** Placeholder text rendered inside the input when it has no value. */
124
+ placeholder?: string;
125
+ /** Helper text rendered below the field to provide additional context. */
126
+ description?: string;
127
+ /** Static list of options for `select` / enum fields. */
128
+ options?: SelectOption[];
129
+ /** Group the field under a named section in the form layout. */
130
+ section?: string;
131
+ /** Explicit render order within the form or section (lower numbers render first). */
132
+ order?: number;
133
+ /** Grid column span for multi-column layouts (e.g. `1`–`12`). */
134
+ span?: number;
135
+ /** When `true`, the field is not rendered. */
136
+ hidden?: boolean;
137
+ /** When `true`, the field is rendered but not interactive. */
138
+ disabled?: boolean;
139
+ /** Conditionally show or hide the field based on the current form values. */
140
+ condition?: FieldCondition;
141
+ /**
142
+ * Override the component used to render this field.
143
+ *
144
+ * - **string** — a key registered in the `ComponentRegistry` (e.g. `'autocomplete'`
145
+ * registered via `createAutoForm({ components: { autocomplete: MyComp } })` or the
146
+ * `components` prop).
147
+ * - **React component** — a `FieldProps`-compatible component passed inline,
148
+ * bypassing the registry entirely (e.g. `component: MyCustomInput`).
149
+ *
150
+ * Note: typed as `React.ComponentType<any>` here to avoid a circular type
151
+ * reference through `FieldProps → FieldMeta → component → FieldProps`.
152
+ * The `ComponentRegistry` keeps the stricter `React.ComponentType<FieldProps>`.
153
+ */
154
+ component?: string | React.ComponentType<any>;
155
+ /** Called when this field's value changes. Receives the new value and form control methods. May be async. */
156
+ onChange?: (value: unknown, form: FormMethods) => void | Promise<void>;
157
+ /** When `true`, rows in an array field can be reordered via move-up/move-down buttons. */
158
+ movable?: boolean;
159
+ /** When `true`, rows in an array field can be duplicated. */
160
+ duplicable?: boolean;
161
+ /** When `true`, rows in an array field can be individually collapsed. */
162
+ collapsible?: boolean;
163
+ };
164
+ /**
165
+ * Per-field UI metadata with an open index signature, allowing arbitrary
166
+ * extra keys for custom component use-cases. Extends `FieldMetaBase` with
167
+ * all the standard metadata properties.
168
+ */
169
+ type FieldMeta = FieldMetaBase & {
170
+ [key: string]: unknown;
171
+ };
172
+ /**
173
+ * Common properties shared by every field variant.
174
+ */
175
+ type FieldConfigBase = {
176
+ /** Dot-notated field path (e.g. `"address.street"`). */
177
+ name: string;
178
+ /** Display label for the field. */
179
+ label: string;
180
+ /** Whether the field is required by the schema. */
181
+ required: boolean;
182
+ /** Merged UI metadata for the field. */
183
+ meta: FieldMeta;
184
+ };
185
+ /**
186
+ * The fully resolved configuration for a single form field, produced by
187
+ * introspecting the Zod schema and merging any `fields` prop overrides.
188
+ * Consumed internally by field renderer components.
189
+ *
190
+ * This is a discriminated union on the `type` field — narrow on `type` to
191
+ * access the fields that are only present for specific field kinds (e.g.
192
+ * `children` for `"object"`, `itemConfig` for `"array"`, etc.).
193
+ */
194
+ type FieldConfig = FieldConfigBase & ({
195
+ type: 'string';
196
+ } | {
197
+ type: 'number';
198
+ } | {
199
+ type: 'boolean';
200
+ } | {
201
+ type: 'date';
202
+ } | {
203
+ type: 'select';
204
+ /** Resolved options for `select` / enum fields. */
205
+ options: SelectOption[];
206
+ } | {
207
+ type: 'object';
208
+ /** Child field configs for nested object fields. */
209
+ children: FieldConfig[];
210
+ } | {
211
+ type: 'array';
212
+ /** Item field config describing a single row's shape. */
213
+ itemConfig: FieldConfig;
214
+ /** Minimum number of items (from `z.array().min(...)`). */
215
+ minItems?: number;
216
+ /** Maximum number of items (from `z.array().max(...)`). */
217
+ maxItems?: number;
218
+ } | {
219
+ type: 'union';
220
+ /** Variant configs for each union member. */
221
+ unionVariants: FieldConfig[];
222
+ /** Discriminator key for discriminated unions. */
223
+ discriminatorKey?: string;
224
+ } | {
225
+ type: 'unknown';
226
+ });
227
+ /**
228
+ * The props passed to every field renderer component. Provides the current
229
+ * value, change/blur handlers, and all resolved UI metadata needed to render
230
+ * a single field.
231
+ */
232
+ type FieldProps = {
233
+ /** Dot-notated field path (e.g. `"address.street"`). */
234
+ name: string;
235
+ /** The current field value. */
236
+ value: unknown;
237
+ /** Callback to update the field value. */
238
+ onChange: (value: unknown) => void;
239
+ /** Callback fired when the field loses focus. */
240
+ onBlur: () => void;
241
+ /** Ref callback for registering the DOM element with `react-hook-form`. */
242
+ ref: RefCallBack;
243
+ /** Resolved display label for the field. */
244
+ label: string;
245
+ /** Placeholder text for the input. */
246
+ placeholder?: string;
247
+ /** Helper text rendered below the field. */
248
+ description?: string;
249
+ /** Validation error message for the field. */
250
+ error?: string;
251
+ /** Whether the field is required by the schema. */
252
+ required: boolean;
253
+ /** When `true`, the field is rendered but not interactive. */
254
+ disabled?: boolean;
255
+ /** Resolved options for `select` / enum fields. */
256
+ options?: SelectOption[];
257
+ /** Full field metadata, including any custom keys. */
258
+ meta: FieldMeta;
259
+ };
260
+ /**
261
+ * A map of field type keys to React components used to render them.
262
+ * Built-in keys (`string`, `number`, `boolean`, `date`, `select`, `textarea`)
263
+ * are pre-typed. Additional custom keys can be added via the index signature
264
+ * and registered through `createAutoForm` or the `components` prop.
265
+ */
266
+ type ComponentRegistry = {
267
+ string?: React.ComponentType<FieldProps>;
268
+ number?: React.ComponentType<FieldProps>;
269
+ boolean?: React.ComponentType<FieldProps>;
270
+ date?: React.ComponentType<FieldProps>;
271
+ select?: React.ComponentType<FieldProps>;
272
+ textarea?: React.ComponentType<FieldProps>;
273
+ [key: string]: React.ComponentType<FieldProps> | undefined;
274
+ };
275
+ /**
276
+ * Props passed to the field wrapper component that surrounds every rendered
277
+ * field. Used to render the label, description, error message, and grid span.
278
+ */
279
+ type FieldWrapperProps = {
280
+ /** The field input component to wrap. */
281
+ children: React.ReactNode;
282
+ /** The fully resolved field configuration. */
283
+ field: FieldConfig;
284
+ /** Validation error message for the field. */
285
+ error?: string;
286
+ /** Grid column span override (takes precedence over `field.meta.span`). */
287
+ span?: number;
288
+ /**
289
+ * Zero-based render index of this field within its parent container
290
+ * (form root or section). Exposed as the `--field-index` CSS custom property.
291
+ */
292
+ index?: number;
293
+ /**
294
+ * Nesting depth of this field (0 = top-level, 1 = inside object, etc.).
295
+ * Exposed as the `--field-depth` CSS custom property.
296
+ */
297
+ depth?: number;
298
+ };
299
+ /**
300
+ * Props passed to the component that renders a single row inside an array field,
301
+ * including the row's content and action buttons (move, duplicate, remove, collapse).
302
+ */
303
+ type ArrayRowLayoutProps = {
304
+ /** The rendered fields for this array item. */
305
+ children: React.ReactNode;
306
+ /** Action button nodes for the row. */
307
+ buttons: {
308
+ /** Button to move the row up, or `null` if already first. */
309
+ moveUp: React.ReactNode | null;
310
+ /** Button to move the row down, or `null` if already last. */
311
+ moveDown: React.ReactNode | null;
312
+ /** Button to duplicate the row, or `null` if at max items. */
313
+ duplicate: React.ReactNode | null;
314
+ /** Button to remove the row. */
315
+ remove: React.ReactNode;
316
+ /** Button to collapse/expand the row, or `null` if collapsing is disabled. */
317
+ collapse: React.ReactNode | null;
318
+ };
319
+ /** Zero-based index of this row within the array. */
320
+ index: number;
321
+ /** Total number of rows currently in the array. */
322
+ rowCount: number;
323
+ };
324
+ /**
325
+ * Optional layout slot overrides for top-level structural components of the
326
+ * form. Provide only the slots you want to replace; omitted slots fall back
327
+ * to the built-in defaults.
328
+ */
329
+ type LayoutSlots = {
330
+ /** Wrapper rendered around the entire form. */
331
+ formWrapper?: React.ComponentType<{
332
+ children: React.ReactNode;
333
+ }>;
334
+ /** Wrapper rendered around each named field section. */
335
+ sectionWrapper?: React.ComponentType<{
336
+ children: React.ReactNode;
337
+ title: string;
338
+ }>;
339
+ /** Custom submit button component. */
340
+ submitButton?: React.ComponentType<{
341
+ isSubmitting: boolean;
342
+ label: string;
343
+ }>;
344
+ /** Custom layout component for individual rows in array fields. */
345
+ arrayRowLayout?: React.ComponentType<ArrayRowLayoutProps>;
346
+ /**
347
+ * Content rendered while async `defaultValues` are loading.
348
+ * Defaults to a simple `<p>Loading…</p>` when not provided.
349
+ */
350
+ loadingFallback?: React.ReactNode;
351
+ };
352
+ /**
353
+ * The resolved version of `LayoutSlots` used internally, where all slots are
354
+ * guaranteed to be defined (falling back to built-in defaults).
355
+ */
356
+ type ResolvedLayoutSlots = {
357
+ formWrapper: React.ComponentType<{
358
+ children: React.ReactNode;
359
+ }>;
360
+ sectionWrapper: React.ComponentType<{
361
+ children: React.ReactNode;
362
+ title: string;
363
+ }>;
364
+ submitButton: React.ComponentType<{
365
+ isSubmitting: boolean;
366
+ label: string;
367
+ }>;
368
+ arrayRowLayout: React.ComponentType<ArrayRowLayoutProps>;
369
+ loadingFallback: React.ReactNode;
370
+ };
371
+ /**
372
+ * CSS class name overrides for the various structural elements of the form.
373
+ * Only the keys you provide will be applied; omitted keys use the built-in
374
+ * default class names (or none, if the default components don't apply any).
375
+ */
376
+ type FormClassNames = {
377
+ /** Class applied to the `<form>` element. */
378
+ form?: string;
379
+ /** Class applied to each field wrapper. */
380
+ fieldWrapper?: string;
381
+ /** Class applied to each field label. */
382
+ label?: string;
383
+ /** Class applied to each field description. */
384
+ description?: string;
385
+ /** Class applied to each field error message. */
386
+ error?: string;
387
+ /** Class applied to the "add item" button in array fields. */
388
+ arrayAdd?: string;
389
+ /** Class applied to the "remove item" button in array fields. */
390
+ arrayRemove?: string;
391
+ /** Class applied to the "move item" buttons in array fields. */
392
+ arrayMove?: string;
393
+ /** Class applied to the "duplicate item" button in array fields. */
394
+ arrayDuplicate?: string;
395
+ /** Class applied to the "collapse item" button in array fields. */
396
+ arrayCollapse?: string;
397
+ };
398
+ /**
399
+ * A per-field override entry used in the AutoFormProps `fields` prop.
400
+ * The `onChange` callback is typed to the specific schema's inferred value
401
+ * type, providing full IDE autocomplete.
402
+ */
403
+ type FieldOverride<TSchema extends z.$ZodObject = z.$ZodObject, TValue = unknown> = Partial<FieldMetaBase> & {
404
+ /** Conditionally show or hide the field based on the current form values. */
405
+ condition?: FieldCondition<z.infer<TSchema>>;
406
+ /** Called when this field's value changes. Receives the new value and form control methods. May be async. */
407
+ onChange?: (value: TValue, form: FormMethods<z.infer<TSchema>>) => void | Promise<void>;
408
+ [key: string]: unknown;
409
+ };
410
+ type FormLabels = {
411
+ /** Submit button text — default: "Submit" */
412
+ submit?: string;
413
+ /** Array "Add item" button — default: "Add" */
414
+ arrayAdd?: string;
415
+ /** Array "Remove row" button — default: "Remove" */
416
+ arrayRemove?: string;
417
+ /** Array "Move row up" button — default: "↑" */
418
+ arrayMoveUp?: string;
419
+ /** Array "Move row down" button — default: "↓" */
420
+ arrayMoveDown?: string;
421
+ /** Array "Duplicate row" button — default: "Duplicate" */
422
+ arrayDuplicate?: string;
423
+ /** Array row toggle shown when the row is expanded (clicking collapses it) — default: "▼" */
424
+ arrayCollapse?: string;
425
+ /** Array row toggle shown when the row is collapsed (clicking expands it) — default: "▶" */
426
+ arrayExpand?: string;
427
+ };
428
+ /**
429
+ * A map of field names to coercion functions. Each function receives the raw
430
+ * field value and returns the coerced value before Zod validation is applied.
431
+ * Useful for transforming string inputs (e.g. from native `<input>`) into the
432
+ * types expected by the schema (e.g. numbers, dates).
433
+ */
434
+ type CoercionMap$1 = Record<string, (value: unknown) => unknown>;
435
+ /**
436
+ * Custom validation error message overrides. Use `required` to override the
437
+ * global "required field" message, or provide a field name key to override
438
+ * messages for a specific field (supports nested dot-notated paths).
439
+ */
440
+ type ValidationMessages = {
441
+ required?: string;
442
+ [fieldName: string]: string | Record<string, string> | undefined;
443
+ };
444
+ /**
445
+ * A minimal storage adapter interface compatible with `localStorage` and
446
+ * `sessionStorage`. Provide a custom implementation to persist form values
447
+ * to any backing store (e.g. IndexedDB, AsyncStorage).
448
+ */
449
+ type PersistStorage = {
450
+ getItem: (key: string) => string | null;
451
+ setItem: (key: string, value: string) => void;
452
+ removeItem: (key: string) => void;
453
+ };
454
+ /**
455
+ * The imperative handle exposed via `ref` on `<AutoForm>`. Provides methods
456
+ * to programmatically control the form from a parent component.
457
+ *
458
+ * @template TSchema - The Zod object schema that defines the form shape.
459
+ */
460
+ type AutoFormHandle<TSchema extends z.$ZodObject = z.$ZodObject> = FormMethods<z.infer<TSchema>> & {
461
+ /** `true` while an async `onSubmit` handler is in flight. */
462
+ isSubmitting: boolean;
463
+ };
464
+ /**
465
+ * Static configuration provided to `createAutoForm`. These options become the
466
+ * default for every form instance created by the factory, and can be
467
+ * overridden per-instance via the corresponding `<AutoForm>` props.
468
+ */
469
+ type AutoFormConfig = {
470
+ /** Default component registry for all form instances. */
471
+ components?: ComponentRegistry;
472
+ /** Default field wrapper component for all form instances. */
473
+ fieldWrapper?: React.ComponentType<FieldWrapperProps>;
474
+ /** Default layout slot overrides for all form instances. */
475
+ layout?: LayoutSlots;
476
+ /** Default CSS class name overrides for all form instances. */
477
+ classNames?: FormClassNames;
478
+ /** When `true`, all fields in every form instance are disabled by default. */
479
+ disabled?: boolean;
480
+ /** Default coercion map applied to all form instances. */
481
+ coercions?: CoercionMap$1;
482
+ /** Default validation message overrides for all form instances. */
483
+ messages?: ValidationMessages;
484
+ /** Default label strings; overridden per-instance by the `labels` prop */
485
+ labels?: FormLabels;
486
+ };
487
+ /**
488
+ * Props for the `<AutoForm>` component. Drives schema introspection, field
489
+ * rendering, validation, and submission.
490
+ *
491
+ * @template TSchema - The Zod object schema that defines the form shape.
492
+ */
493
+ type AutoFormProps<TSchema extends z.$ZodObject> = {
494
+ /** A UniForm instance carrying the schema and typed onChange handlers. */
495
+ form: {
496
+ readonly schema: TSchema;
497
+ };
498
+ /** Called with the validated form values when the form is submitted successfully. */
499
+ onSubmit: (values: z.infer<TSchema>) => void | Promise<void>;
500
+ /**
501
+ * Initial values to pre-populate the form with.
502
+ * When an async function is provided, the form shows `loadingFallback` until the
503
+ * promise resolves, then resets the form with the loaded values.
504
+ */
505
+ defaultValues?: Partial<z.infer<TSchema>> | (() => Promise<Partial<z.infer<TSchema>>>);
506
+ /** Component registry overrides for this form instance. */
507
+ components?: ComponentRegistry;
508
+ /** Per-field UI metadata overrides (label, placeholder, options, etc.). */
509
+ fields?: {
510
+ [K in DeepKeys<z.infer<TSchema>>]?: FieldOverride<TSchema, DeepFieldValue<z.infer<TSchema>, K>>;
511
+ };
512
+ /** Field wrapper component override for this form instance. */
513
+ fieldWrapper?: React.ComponentType<FieldWrapperProps>;
514
+ /** Layout slot overrides for this form instance. */
515
+ layout?: LayoutSlots;
516
+ /** CSS class name overrides for this form instance. */
517
+ classNames?: FormClassNames;
518
+ /** When `true`, all fields are rendered in a disabled (non-interactive) state. */
519
+ disabled?: boolean;
520
+ /** Coercion map applied before Zod validation for this form instance. */
521
+ coercions?: CoercionMap$1;
522
+ /** Validation message overrides for this form instance. */
523
+ messages?: ValidationMessages;
524
+ /** When set, form values are auto-saved to storage under this key */
525
+ persistKey?: string;
526
+ /** Debounce interval in ms for persistence writes (default: 300) */
527
+ persistDebounce?: number;
528
+ /** Custom storage adapter (default: localStorage) */
529
+ persistStorage?: PersistStorage;
530
+ /** Called on every value change with the current form values */
531
+ onValuesChange?: (values: z.infer<TSchema>) => void;
532
+ /** Customize hard-coded UI text (submit button, array buttons, etc.) */
533
+ labels?: FormLabels;
534
+ };
535
+
536
+ // zod@3.25+ — import from 'zod/v4'
537
+ declare module 'zod/v4/core' {
538
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type
539
+ interface GlobalMeta extends FieldMetaBase {}
540
+ }
541
+
542
+ // zod@4.x — import from 'zod'
543
+ declare module 'zod' {
544
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type
545
+ interface GlobalMeta extends FieldMetaBase {}
546
+ }
547
+
548
+ /**
549
+ * Recursively introspects a Zod schema and returns a {@link FieldConfig}
550
+ * describing the field's type, label, validation constraints, and UI metadata.
551
+ *
552
+ * Transparent wrappers (`optional`, `nullable`, `default`, `pipe`) are
553
+ * unwrapped before inspection. Unknown/unsupported types fall back to
554
+ * `type: 'unknown'` rather than throwing.
555
+ *
556
+ * @param schema - The Zod schema to introspect.
557
+ * @param name - The field key within its parent object (used for label derivation).
558
+ * @param parentPath - The dot-notated path of the parent (used to build `field.name`).
559
+ */
560
+ declare function introspectSchema(schema: z.$ZodType, name?: string, parentPath?: string): FieldConfig;
561
+ /**
562
+ * Introspects all fields of a top-level `ZodObject` schema and returns an
563
+ * ordered array of {@link FieldConfig} objects, one per key in `schema.shape`.
564
+ *
565
+ * This is the entry point used by `<AutoForm>` to derive the field list from
566
+ * the provided schema.
567
+ *
568
+ * @param schema - The top-level `ZodObject` schema to introspect.
569
+ */
570
+ declare function introspectObjectSchema(schema: z.$ZodObject): FieldConfig[];
571
+
572
+ /**
573
+ * The core auto-form component. Introspects the provided Zod `schema`,
574
+ * renders the appropriate field components, validates on submit using
575
+ * `zodResolver`, and calls `onSubmit` with the fully-typed, validated values.
576
+ *
577
+ * Supports: conditional fields, dynamic field meta via UniForm onChange
578
+ * handlers, section grouping, form persistence, imperative handle via `ref`,
579
+ * and full layout/component customisation.
580
+ *
581
+ * @template TSchema - A `ZodObject` schema that defines the form shape.
582
+ *
583
+ * @example
584
+ * const myForm = new UniForm(z.object({ name: z.string(), age: z.number() }))
585
+ *
586
+ * <AutoForm form={myForm} onSubmit={(values) => console.log(values)} />
587
+ */
588
+ declare function AutoForm<TSchema extends z.$ZodObject>(props: AutoFormProps<TSchema> & {
589
+ ref?: React.Ref<AutoFormHandle<TSchema>>;
590
+ }): react_jsx_runtime.JSX.Element;
591
+
592
+ type FieldRendererProps = {
593
+ field: FieldConfig;
594
+ control: Control;
595
+ namePrefix?: string;
596
+ /** Zero-based render index within the parent container (form root / section / object). */
597
+ index?: number;
598
+ /** Nesting depth (0 = top-level, 1 = inside object, etc.). */
599
+ depth?: number;
600
+ /** When `true`, unregisters the field's value on unmount so it resets to its default when reshown.
601
+ * Auto-set for conditional fields; propagated to object children. */
602
+ shouldUnregister?: boolean;
603
+ };
604
+ /**
605
+ * Renders a single form field by delegating to the appropriate field component
606
+ * based on `field.type`. Object and array fields manage their own layout and
607
+ * skip the field wrapper; all other fields are wrapped in the configured
608
+ * `FieldWrapper` with their resolved error message.
609
+ */
610
+ declare function FieldRenderer({ field, control, namePrefix, index, depth, shouldUnregister, }: FieldRendererProps): react_jsx_runtime.JSX.Element | null;
611
+
612
+ declare function DefaultInput(props: FieldProps): react_jsx_runtime.JSX.Element;
613
+
614
+ declare function DefaultCheckbox(props: FieldProps): react_jsx_runtime.JSX.Element;
615
+
616
+ declare function DefaultSelect(props: FieldProps): react_jsx_runtime.JSX.Element;
617
+
618
+ declare function DefaultFieldWrapper({ children, field, error, span, index, depth, }: FieldWrapperProps): react_jsx_runtime.JSX.Element;
619
+
620
+ type DefaultSubmitButtonProps = {
621
+ isSubmitting: boolean;
622
+ };
623
+ declare function DefaultSubmitButton({ isSubmitting, }: DefaultSubmitButtonProps): react_jsx_runtime.JSX.Element;
624
+
625
+ declare const defaultRegistry: ComponentRegistry;
626
+
627
+ /**
628
+ * Merges two {@link ComponentRegistry} objects, with `overrides` taking
629
+ * precedence over `base` for any keys that appear in both.
630
+ *
631
+ * Returns `base` unchanged when `overrides` is `undefined`.
632
+ *
633
+ * @param base - The default registry (typically the factory-level registry).
634
+ * @param overrides - Optional per-instance registry to merge on top.
635
+ */
636
+ declare function mergeRegistries(base: ComponentRegistry, overrides?: ComponentRegistry): ComponentRegistry;
637
+
638
+ /**
639
+ * Factory that creates a pre-configured `<AutoForm>` component with a fixed
640
+ * set of defaults baked in.
641
+ *
642
+ * All options passed to `createAutoForm` become the baseline for every form
643
+ * instance produced by the returned component. Any prop passed directly to the
644
+ * returned component is **deep-merged** on top of those defaults (instance
645
+ * props win on conflicts).
646
+ *
647
+ * Merged items: `components`, `layout`, `classNames`, `coercions`, `messages`.
648
+ * Replaced items: `fieldWrapper`, `disabled` (OR-ed with the factory default).
649
+ *
650
+ * @param config - Factory-level defaults applied to every form instance.
651
+ * @returns A `<AutoForm>`-compatible component with the provided defaults applied.
652
+ *
653
+ * @example
654
+ * const AutoForm = createAutoForm({
655
+ * components: { string: MyTextInput },
656
+ * classNames: { form: 'my-form' },
657
+ * })
658
+ *
659
+ * // Later:
660
+ * <AutoForm form={myUniForm} onSubmit={handleSubmit} />
661
+ */
662
+ declare function createAutoForm(config: AutoFormConfig): {
663
+ <TSchema extends z.$ZodObject>(props: AutoFormProps<TSchema> & {
664
+ ref?: React.Ref<AutoFormHandle<TSchema>>;
665
+ }): react_jsx_runtime.JSX.Element;
666
+ displayName: string;
667
+ };
668
+
669
+ type CoercionMap = Record<string, (value: unknown) => unknown>;
670
+ /**
671
+ * Built-in coercion functions for the primitive field types. Each function
672
+ * converts the raw string value that comes from an HTML `<input>` into the
673
+ * type expected by the Zod schema before validation is applied.
674
+ *
675
+ * - `number` — converts to `Number`; returns `undefined` for empty/null inputs.
676
+ * - `date` — converts to `Date`; returns `undefined` for empty/null inputs.
677
+ * - `boolean` — converts to `Boolean`.
678
+ * - `string` — converts `null` / `undefined` to `''`, otherwise `String(value)`.
679
+ */
680
+ declare const defaultCoercionMap: CoercionMap;
681
+ /**
682
+ * Coerces `value` to the type expected by `type`, using `customCoercions` first
683
+ * and falling back to {@link defaultCoercionMap}.
684
+ *
685
+ * Returns the value unchanged when no coercion function is found for the given type.
686
+ *
687
+ * @param type - The field type string (e.g. `'number'`, `'date'`).
688
+ * @param value - The raw value to coerce.
689
+ * @param customCoercions - Optional per-instance overrides that take precedence
690
+ * over the built-in coercion map.
691
+ */
692
+ declare function coerceValue(type: string, value: unknown, customCoercions?: CoercionMap): unknown;
693
+
694
+ /**
695
+ * Context passed to UniForm `setOnChange` handlers. Extends `FormMethods` with
696
+ * `setFieldMeta`, which lets handlers dynamically override per-field UI
697
+ * properties (hidden, disabled, options, label, etc.).
698
+ *
699
+ * @template TSchema - The Zod object schema that defines the form shape.
700
+ */
701
+ type UniFormContext<TSchema extends z.$ZodObject = z.$ZodObject> = FormMethods<z.infer<TSchema>> & {
702
+ /**
703
+ * Dynamically override per-field UI metadata from inside a setOnChange handler.
704
+ * Changes are applied synchronously and trigger a re-render.
705
+ *
706
+ * Meta keys are stored and merged into the rendered field config.
707
+ */
708
+ setFieldMeta: <K extends DeepKeys<z.infer<TSchema>>>(field: K, meta: Partial<FieldDependencyResult>) => void;
709
+ };
710
+ type Handler<TSchema extends z.$ZodObject, TValue> = (value: TValue, ctx: UniFormContext<TSchema>) => void | Promise<void>;
711
+ type Condition<TSchema extends z.$ZodObject> = (values: z.infer<TSchema>) => boolean;
712
+ /**
713
+ * A type-safe form definition that lives outside React components.
714
+ * Wraps a Zod schema and lets you attach typed `setOnChange` callbacks that fire
715
+ * whenever a specific field's value changes.
716
+ *
717
+ * Callbacks receive the new field value (typed to the schema) and a
718
+ * `UniFormContext` that provides all standard form methods plus `setFieldMeta`
719
+ * for dynamic field overrides.
720
+ *
721
+ * @template TSchema - The Zod object schema that defines the form shape.
722
+ * @template TRegistered - Union of field keys that already have a `setOnChange`
723
+ * handler registered. Attempting to call `setOnChange` for a key in this set
724
+ * produces a compile-time error, preventing silent handler replacement.
725
+ *
726
+ * @example
727
+ * const addressForm = new UniForm(addressSchema)
728
+ * .setOnChange('country', (value, ctx) => {
729
+ * ctx.setFieldMeta('state', { hidden: value !== 'US' })
730
+ * })
731
+ *
732
+ * // In component:
733
+ * <AutoForm form={addressForm} onSubmit={handleSubmit} />
734
+ */
735
+ declare class UniForm<TSchema extends z.$ZodObject, TRegistered extends string = never> {
736
+ readonly schema: TSchema;
737
+ private readonly _handlers;
738
+ private readonly _conditions;
739
+ constructor(schema: TSchema);
740
+ /**
741
+ * Set the typed onChange handler for a specific field.
742
+ * Replaces any previously registered handler for that field — only one
743
+ * handler per field is kept. This prevents accidental handler accumulation
744
+ * when called inside a React render cycle.
745
+ * Returns `this` for fluent chaining.
746
+ */
747
+ setOnChange<K extends Exclude<DeepKeys<z.infer<TSchema>>, TRegistered>>(field: K, handler: Handler<TSchema, DeepFieldValue<z.infer<TSchema>, K>>): UniForm<TSchema, TRegistered | K>;
748
+ /**
749
+ * Attach a typed condition for a specific field.
750
+ * The field is shown when the predicate returns `true`, hidden when `false`.
751
+ * Composes with any `condition` set via the `fields` prop (UniForm takes precedence).
752
+ * Returns `this` for fluent chaining.
753
+ */
754
+ setCondition<K extends DeepKeys<z.infer<TSchema>>>(field: K, predicate: Condition<TSchema>): this;
755
+ /** @internal Called by AutoForm to fire the handler registered for a field. */
756
+ _fireHandler(field: string, value: unknown, ctx: UniFormContext<TSchema>): void | Promise<void>;
757
+ /** @internal Returns all field names that have registered onChange handlers. */
758
+ _getWatchedFields(): string[];
759
+ /** @internal Returns a copy of the conditions map for AutoForm to inject into field meta. */
760
+ _getConditions(): Map<string, Condition<TSchema>>;
761
+ }
762
+ /**
763
+ * Creates a new `UniForm` instance for the given Zod object schema.
764
+ */
765
+ declare function createForm<TSchema extends z.$ZodObject>(schema: TSchema): UniForm<TSchema>;
766
+ /**
767
+ * Creates a `UniForm` directly from a `z.discriminatedUnion` schema.
768
+ *
769
+ * `AutoForm` automatically flattens the variant fields and attaches show/hide
770
+ * conditions based on the discriminator value — no manual `.condition()` calls
771
+ * needed. The union schema is used by `zodResolver` for strict per-variant
772
+ * validation on submit.
773
+ *
774
+ * @example
775
+ * const notificationForm = createForm(
776
+ * z.discriminatedUnion('channel', [
777
+ * z.object({ channel: z.literal('email'), email: z.string().email() }),
778
+ * z.object({ channel: z.literal('sms'), phone: z.string() }),
779
+ * ])
780
+ * )
781
+ */
782
+ declare function createForm(schema: z.$ZodDiscriminatedUnion): UniForm<z.$ZodObject>;
783
+
784
+ /**
785
+ * Filters and sorts a list of field configs based on the current form values.
786
+ *
787
+ * - Fields with `meta.hidden === true` are always excluded.
788
+ * - Fields with a `meta.condition` function are included only when the
789
+ * function returns `true` for the current values.
790
+ * - Remaining fields are sorted ascending by `meta.order` (fields without an
791
+ * order appear last).
792
+ *
793
+ * Re-evaluates reactively whenever the watched form values change.
794
+ *
795
+ * @param fields - The full list of field configs to filter and sort.
796
+ * @param control - The RHF `control` object from the parent form.
797
+ * @returns The filtered and ordered subset of `fields`.
798
+ */
799
+ declare function useConditionalFields(fields: FieldConfig[], control: Control): FieldConfig[];
800
+
801
+ type SectionGroup = {
802
+ title: string | null;
803
+ fields: FieldConfig[];
804
+ };
805
+ /**
806
+ * Groups a flat list of field configs into ordered section groups based on
807
+ * each field's `meta.section` value.
808
+ *
809
+ * - Fields without a `meta.section` (or with a non-string value) are placed
810
+ * in an "ungrouped" section with `title: null`, rendered first.
811
+ * - Named sections appear in the order their first member is encountered.
812
+ * - The returned array is memoized and only recomputed when `fields` changes.
813
+ *
814
+ * @param fields - The ordered list of (already filtered/sorted) field configs.
815
+ * @returns An array of {@link SectionGroup} objects ready to be rendered.
816
+ */
817
+ declare function useSectionGrouping(fields: FieldConfig[]): SectionGroup[];
818
+
819
+ /**
820
+ * Persists form values to a storage adapter and restores them on mount.
821
+ *
822
+ * - On mount, reads `key` from `storage` and calls `reset` with the merged
823
+ * stored + default values, so the form starts with any previously saved data.
824
+ * - On every value change, writes the current form values to `storage` after a
825
+ * `debounceMs` delay to avoid thrashing the storage layer.
826
+ * - When `key` is `undefined`, persistence is entirely disabled.
827
+ * - Falls back to `sessionStorage` when no custom `storage` adapter is provided.
828
+ *
829
+ * @returns An object with `clearPersistedData` — call this after a successful
830
+ * submission to remove the persisted draft.
831
+ */
832
+ declare function useFormPersistence(options: {
833
+ control: Control;
834
+ key: string | undefined;
835
+ debounceMs: number;
836
+ storage?: PersistStorage;
837
+ reset: (values: Record<string, unknown>) => void;
838
+ defaultValues: Record<string, unknown>;
839
+ }): {
840
+ clearPersistedData: () => void;
841
+ };
842
+
843
+ type AutoFormContextValue = {
844
+ registry: ComponentRegistry;
845
+ fieldOverrides: Record<string, unknown>;
846
+ fieldWrapper: React.ComponentType<FieldWrapperProps>;
847
+ layout: ResolvedLayoutSlots;
848
+ classNames: FormClassNames;
849
+ disabled: boolean;
850
+ coercions?: CoercionMap$1;
851
+ messages?: ValidationMessages;
852
+ labels: FormLabels;
853
+ formMethods: FormMethods;
854
+ };
855
+ declare function useAutoFormContext(): AutoFormContextValue;
856
+
857
+ export { type ArrayRowLayoutProps, AutoForm, type AutoFormConfig, type AutoFormContextValue, type AutoFormHandle, type AutoFormProps, type CoercionMap$1 as CoercionMap, type ComponentRegistry, DefaultCheckbox, DefaultFieldWrapper, DefaultInput, DefaultSelect, DefaultSubmitButton, type FieldCondition, type FieldConfig, type FieldDependencyResult, type FieldMeta, type FieldMetaBase, type FieldOverride, type FieldProps, FieldRenderer, type FieldType, type FieldWrapperProps, type FormClassNames, type FormLabels, type FormMethods, type LayoutSlots, type PersistStorage, type ResolvedLayoutSlots, type SectionGroup, type SelectOption, UniForm, type UniFormContext, type ValidationMessages, coerceValue, createAutoForm, createForm, defaultCoercionMap, defaultRegistry, introspectObjectSchema, introspectSchema, mergeRegistries, useAutoFormContext, useConditionalFields, useFormPersistence, useSectionGrouping };