@formdef/core 0.2.0 → 0.3.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.
package/dist/index.cjs CHANGED
@@ -683,7 +683,7 @@ function errorsOf(field) {
683
683
  const PATH_SEPARATORS = /[.[\]]+/g;
684
684
  const SELECTOR_SAFE_SEPARATOR = "__";
685
685
  /**
686
- * Builds the DOM id that `fieldParts` gives the control of a field, for code that has no
686
+ * Builds the DOM id that `useFieldParts` gives the control of a field, for code that has no
687
687
  * field at hand — an error summary above the form that links to each invalid field, for example.
688
688
  *
689
689
  * The id is the form's id, a hyphen, and the field's path with every run of dots and brackets
@@ -710,17 +710,42 @@ function fieldId(formId, path) {
710
710
  const DESCRIPTION_SUFFIX = "-description";
711
711
  const ERROR_SUFFIX = "-error";
712
712
  const IDREF_SEPARATOR = " ";
713
- function isErrorDueFor(field) {
713
+ function isErrorDueFor(field, wasSubmitted) {
714
714
  const { isBlurred, isDirty, isValid } = field.state.meta;
715
- const wasEditedAndLeft = isBlurred && isDirty;
716
- const wasSubmitted = field.form.state.submissionAttempts > 0;
717
- return !isValid && (wasEditedAndLeft || wasSubmitted);
715
+ return !isValid && (isBlurred && isDirty || wasSubmitted);
718
716
  }
719
717
  function idrefsOf(ids) {
720
718
  const present = ids.filter((id) => id !== void 0);
721
719
  return present.length > 0 ? present.join(IDREF_SEPARATOR) : void 0;
722
720
  }
723
721
  /**
722
+ * The parts of `field` with the submit state passed in, so the result depends on nothing the
723
+ * caller cannot see.
724
+ */
725
+ function fieldPartsFor(field, options) {
726
+ const id = fieldId(field.form.formId, field.name);
727
+ const descriptionId = `${id}${DESCRIPTION_SUFFIX}`;
728
+ const errorId = `${id}${ERROR_SUFFIX}`;
729
+ const isErrorDue = isErrorDueFor(field, options.wasSubmitted);
730
+ const errors = errorsOf(field);
731
+ const isErrorNamed = isErrorDue && errors.length > 0;
732
+ const hasDescription = options.description !== void 0 && options.description !== "";
733
+ return {
734
+ control: {
735
+ id,
736
+ name: field.name,
737
+ onBlur: field.handleBlur,
738
+ "aria-invalid": isErrorDue || void 0,
739
+ "aria-describedby": idrefsOf([hasDescription ? descriptionId : void 0, isErrorNamed ? errorId : void 0]),
740
+ "aria-errormessage": isErrorNamed ? errorId : void 0
741
+ },
742
+ descriptionId,
743
+ errorId,
744
+ isErrorDue,
745
+ errors
746
+ };
747
+ }
748
+ /**
724
749
  * Wires a widget's control to its field: its id and name, the blur report, and the aria
725
750
  * attributes that tie it to its description and its errors.
726
751
  *
@@ -729,6 +754,11 @@ function idrefsOf(ids) {
729
754
  * points at the error element through `aria-describedby` and `aria-errormessage`. An id appears in
730
755
  * them only when its element is rendered.
731
756
  *
757
+ * `fieldParts` reads whether the form was submitted without subscribing to it, so a widget whose
758
+ * field state does not change on a submit keeps its error hidden until it renders again.
759
+ *
760
+ * @deprecated Use {@link useFieldParts}, which subscribes to the submit and renders the widget
761
+ * again.
732
762
  * @typeParam TValue - The value type of the field.
733
763
  * @param field - The field the widget renders.
734
764
  * @param options - What the widget renders besides the control.
@@ -755,31 +785,60 @@ function idrefsOf(ids) {
755
785
  * ```
756
786
  */
757
787
  function fieldParts(field, options = {}) {
758
- const id = fieldId(field.form.formId, field.name);
759
- const descriptionId = `${id}${DESCRIPTION_SUFFIX}`;
760
- const errorId = `${id}${ERROR_SUFFIX}`;
761
- const isErrorDue = isErrorDueFor(field);
762
- const errors = errorsOf(field);
763
- const isErrorNamed = isErrorDue && errors.length > 0;
764
- const hasDescription = options.description !== void 0 && options.description !== "";
765
- return {
766
- control: {
767
- id,
768
- name: field.name,
769
- onBlur: field.handleBlur,
770
- "aria-invalid": isErrorDue || void 0,
771
- "aria-describedby": idrefsOf([hasDescription ? descriptionId : void 0, isErrorNamed ? errorId : void 0]),
772
- "aria-errormessage": isErrorNamed ? errorId : void 0
773
- },
774
- descriptionId,
775
- errorId,
776
- isErrorDue,
777
- errors
778
- };
788
+ return fieldPartsFor(field, {
789
+ ...options,
790
+ wasSubmitted: field.form.state.submissionAttempts > 0
791
+ });
792
+ }
793
+ // src/widget/use-field-parts.ts
794
+ /**
795
+ * Wires a widget's control to its field: its id and name, the blur report, and the aria
796
+ * attributes that tie it to its description and its errors.
797
+ *
798
+ * An error is due once the field is invalid and was edited and left, or once the form was
799
+ * submitted. While one is due, `aria-invalid` is `true`; while one with text is due, the control
800
+ * points at the error element through `aria-describedby` and `aria-errormessage`. An id appears in
801
+ * them only when its element is rendered.
802
+ *
803
+ * The hook subscribes to whether the form was submitted, so a submit renders the widget again even
804
+ * when its field state does not change, as for a field that was left before without an edit.
805
+ *
806
+ * @typeParam TValue - The value type of the field.
807
+ * @param field - The field the widget renders.
808
+ * @param options - What the widget renders besides the control.
809
+ * @param options.description - The description the widget renders below its label, if any. Only
810
+ * whether it is a non-empty string is read.
811
+ * @defaultValue `options.description`: `undefined`, no description is rendered.
812
+ * @returns The props for the control, the ids for the description and error elements, whether an
813
+ * error is due, and the error texts.
814
+ *
815
+ * @example
816
+ * ```tsx
817
+ * const TextField = ({ field, label, description }: FieldOf<string> & { label: string; description?: string }) => {
818
+ * const { control, descriptionId, errorId, isErrorDue, errors } = useFieldParts(field, { description })
819
+ *
820
+ * return (
821
+ * <div>
822
+ * <label htmlFor={control.id}>{label}</label>
823
+ * {description && <p id={descriptionId}>{description}</p>}
824
+ * <input {...control} value={field.state.value} onChange={(event) => field.handleChange(event.target.value)} />
825
+ * {isErrorDue && errors.length > 0 && <p id={errorId}>{errors.join(' ')}</p>}
826
+ * </div>
827
+ * )
828
+ * }
829
+ * ```
830
+ */
831
+ function useFieldParts(field, options = {}) {
832
+ const wasSubmitted = (0, _tanstack_react_form.useSelector)(field.form.store, (state) => state.submissionAttempts > 0);
833
+ return fieldPartsFor(field, {
834
+ ...options,
835
+ wasSubmitted
836
+ });
779
837
  }
780
838
  exports.createRegistry = createRegistry;
781
839
  exports.errorsOf = errorsOf;
782
840
  exports.fieldId = fieldId;
783
841
  exports.fieldParts = fieldParts;
842
+ exports.useFieldParts = useFieldParts;
784
843
 
785
844
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["useSelector","Fragment","useRef","getBy","createContext","use","revalidateLogic","createFormHook","createElement"],"sources":["../src/widget/declares-empty-value.ts","../src/render/array-entry.tsx","../src/render/by-name.ts","../src/render/child-path.ts","../src/render/derived.tsx","../src/render/path-pattern.ts","../src/render/required-paths.ts","../src/render/split-entry.ts","../src/render/warn-once.ts","../src/render/fields.tsx","../src/form/find-required-paths.ts","../src/form/blur-then-change.ts","../src/form/form-options-for.ts","../src/form/create-form.tsx","../src/registry/create-registry.ts","../src/widget/errors-of.ts","../src/widget/field-id.ts","../src/widget/field-parts.ts"],"sourcesContent":["/** Whether `widget` declares its empty value, the value it holds while nothing is entered. */\nexport function declaresEmptyValue(widget: object): widget is { emptyValue: unknown } {\n return Object.hasOwn(widget, 'emptyValue')\n}\n","import { type AnyFieldApi, useSelector } from '@tanstack/react-form'\nimport { type ComponentType, Fragment, type ReactNode } from 'react'\n\n// biome-ignore lint/suspicious/noExplicitAny: a registered frame takes props of its own; the config types check them.\ntype AnyProps = any\n\n/** The keys of an array entry the renderer reads besides its item. */\ntype ArrayOptions = {\n newItem?: unknown\n addLabel?: unknown\n removeLabel?: unknown\n itemKey?: unknown\n}\n\ntype ArrayEntryProps = {\n /** The array field, mounted at the entry's path. */\n field: AnyFieldApi\n options: ArrayOptions\n /** The registered `structure.arrayFrame`, if any. */\n frame: ComponentType<AnyProps> | undefined\n /** The entry's props for the frame. */\n props: object | undefined\n renderItem: (index: number, extra?: object) => ReactNode\n}\n\ntype ItemFunction<TResult> = (item: unknown, index?: number) => TResult\n\n/** What the controls of an array entry show: one key and one remove label per item. */\ntype ItemControls = {\n keys: (string | number)[]\n removeLabels: string[]\n}\n\nconst removeLabelOf = (removeLabel: unknown, item: unknown, index: number): string =>\n typeof removeLabel === 'function'\n ? (removeLabel as ItemFunction<string>)(item, index)\n : String(removeLabel)\n\nconst itemControlsOf = (options: ArrayOptions, value: unknown): ItemControls => {\n const items: unknown[] = Array.isArray(value) ? value : []\n\n return {\n keys: items.map((item, index) =>\n typeof options.itemKey === 'function'\n ? (options.itemKey as ItemFunction<string | number>)(item)\n : index,\n ),\n removeLabels: items.map((item, index) => removeLabelOf(options.removeLabel, item, index)),\n }\n}\n\nconst isSameList = <TItem,>(first: readonly TItem[], second: readonly TItem[]): boolean =>\n first.length === second.length && first.every((item, index) => item === second[index])\n\nconst isSameControls = (first: ItemControls, second: ItemControls): boolean =>\n isSameList(first.keys, second.keys) && isSameList(first.removeLabels, second.removeLabels)\n\n/**\n * Renders an array entry: through the registered frame, which receives `ArrayFrameProps`, or as\n * the bare default, each item followed by its remove button and one add button at the end.\n */\nexport const ArrayEntry = ({\n field,\n options,\n frame: Frame,\n props,\n renderItem,\n}: ArrayEntryProps): ReactNode => {\n // The field renders in TanStack's array mode, so only a change to the list reaches it; keys and\n // labels that name an item follow the items through a selection of their own.\n const { keys, removeLabels } = useSelector(\n field.store,\n (state: { value: unknown }) => itemControlsOf(options, state.value),\n { compare: isSameControls },\n )\n const newItem = options.newItem as () => unknown\n const addLabel = String(options.addLabel)\n const add = (): void => field.pushValue(newItem())\n const remove = (index: number): void => field.removeValue(index)\n\n if (Frame === undefined) {\n return (\n <>\n {keys.map((key, index) => (\n <Fragment key={key}>\n {renderItem(index)}\n <button type=\"button\" data-formdef-remove=\"\" onClick={() => remove(index)}>\n {removeLabels[index]}\n </button>\n </Fragment>\n ))}\n <button type=\"button\" data-formdef-add=\"\" onClick={add}>\n {addLabel}\n </button>\n </>\n )\n }\n\n return (\n <Frame\n {...props}\n field={field}\n keys={keys}\n renderItem={renderItem}\n addLabel={addLabel}\n removeLabels={removeLabels}\n add={add}\n remove={remove}\n insert={(index: number, item?: unknown) =>\n field.insertValue(index, item === undefined ? newItem() : item)\n }\n move={(from: number, to: number) => field.moveValue(from, to)}\n swap={(first: number, second: number) => field.swapValues(first, second)}\n replace={(index: number, item: unknown) => field.replaceValue(index, item)}\n clear={() => field.clearValues()}\n />\n )\n}\n","/** What `map` holds under `name` itself; a name every object inherits finds nothing. */\nexport function byName<TValue>(\n map: Readonly<Record<string, TValue>>,\n name: string,\n): TValue | undefined {\n return Object.hasOwn(map, name) ? map[name] : undefined\n}\n","/**\n * The path of `key` below `parent`: a key of the root level by itself, a key of a nested level\n * after a dot, the index of an array item in brackets.\n */\nexport function childPath(parent: string | undefined, key: string | number): string {\n if (typeof key === 'number') {\n return `${parent ?? ''}[${key}]`\n }\n\n return parent === undefined ? key : `${parent}.${key}`\n}\n","import { getBy } from '@tanstack/react-form'\nimport { type ReactNode, useRef } from 'react'\n\nimport type { RenderForm } from './render-form'\n\n/** What an entry reads from the form values: its condition, its props and the paths they read. */\ntype DerivedEntry = {\n visibleWhen?: unknown\n props?: unknown\n dependsOn?: unknown\n}\n\ntype DerivedProps = {\n form: RenderForm\n entry: DerivedEntry\n /** Builds the entry from its props; called only while the entry is visible. */\n children: (props: object | undefined) => ReactNode\n}\n\ntype LastRead = {\n entry: DerivedEntry\n dependencies: unknown[]\n selected: unknown\n}\n\nconst HIDDEN = Symbol('hidden')\nconst VISIBLE = Symbol('visible')\n\nconst propsOf = (props: unknown): object | undefined =>\n typeof props === 'object' && props !== null ? props : undefined\n\nconst isStale = (\n last: LastRead | undefined,\n entry: DerivedEntry,\n dependencies: unknown[],\n): boolean =>\n last === undefined ||\n last.entry !== entry ||\n dependencies.some((value, index) => !Object.is(value, last.dependencies[index]))\n\n/**\n * Renders an entry from what it reads from the form values. An entry with a condition or derived\n * props subscribes to the values; the selected result alone decides whether it re-renders: whether\n * it is shown for a condition, the values for derived props. `dependsOn` narrows when the selector\n * reads again, never what it returns. A hidden entry is never built.\n */\nexport const Derived = ({ form, entry, children }: DerivedProps): ReactNode => {\n const lastRead = useRef<LastRead | undefined>(undefined)\n const { visibleWhen, props, dependsOn } = entry\n const isConditional = typeof visibleWhen === 'function'\n const isDerived = typeof props === 'function'\n\n if (!(isConditional || isDerived)) {\n return children(propsOf(props))\n }\n\n const selectFrom = (values: unknown): unknown => {\n if (isConditional && !visibleWhen(values)) {\n return HIDDEN\n }\n\n return isDerived ? values : VISIBLE\n }\n\n const select = ({ values }: { values: unknown }): unknown => {\n if (!Array.isArray(dependsOn)) {\n return selectFrom(values)\n }\n\n const dependencies = dependsOn.map((path: string) => getBy(values, path))\n if (isStale(lastRead.current, entry, dependencies)) {\n lastRead.current = { entry, dependencies, selected: selectFrom(values) }\n }\n\n return lastRead.current?.selected\n }\n\n return (\n <form.Subscribe selector={select}>\n {(selected) => {\n if (selected === HIDDEN) {\n return null\n }\n\n return children(propsOf(isDerived ? props(selected) : props))\n }}\n </form.Subscribe>\n )\n}\n","const ITEM_INDEX = /\\[\\d+\\]/g\nconst ANY_ITEM = '[]'\n\n/** The path with every array index left open: `contacts[2].name` becomes `contacts[].name`. */\nexport function pathPattern(path: string): string {\n return path.replaceAll(ITEM_INDEX, ANY_ITEM)\n}\n","import { type Context, createContext } from 'react'\n\nexport const NO_REQUIRED_PATHS: ReadonlySet<string> = new Set()\n\n/** The path patterns of the required fields of the level being rendered. */\nexport const RequiredPaths: Context<ReadonlySet<string>> = createContext(NO_REQUIRED_PATHS)\n","/**\n * The keys formdef reads from a field entry. Every other key is one of TanStack's field options and\n * goes to the field unchanged, so an option TanStack adds works without a formdef release.\n */\nexport const OWN_ENTRY_KEYS = [\n 'widget',\n 'props',\n 'fields',\n 'layout',\n 'item',\n 'newItem',\n 'addLabel',\n 'removeLabel',\n 'itemKey',\n 'render',\n 'visibleWhen',\n 'dependsOn',\n] as const\n\n/** A key formdef reads from a field entry. */\nexport type OwnEntryKey = (typeof OWN_ENTRY_KEYS)[number]\n\nconst OWN_ENTRY_KEY_SET: ReadonlySet<string> = new Set(OWN_ENTRY_KEYS)\n\n/** Splits a field entry into the keys formdef reads and TanStack's field options. */\nexport function splitEntry(entry: object): {\n own: Partial<Record<OwnEntryKey, unknown>>\n fieldOptions: Record<string, unknown>\n} {\n const entries = Object.entries(entry)\n\n // `fromEntries` defines each key as data, so a key named `__proto__` cannot set a prototype.\n return {\n own: Object.fromEntries(entries.filter(([key]) => OWN_ENTRY_KEY_SET.has(key))),\n fieldOptions: Object.fromEntries(entries.filter(([key]) => !OWN_ENTRY_KEY_SET.has(key))),\n }\n}\n","// Bundlers replace `process.env.NODE_ENV` in exactly this spelling; without one, `process` may not\n// exist at all.\n// biome-ignore lint/style/useNamingConvention: the name bundlers replace.\ndeclare const process: { env: { NODE_ENV?: string } } | undefined\n\nconst warnedOwners = new WeakSet<object>()\n\nconst isProduction = (): boolean =>\n typeof process !== 'undefined' && process.env.NODE_ENV === 'production'\n\n/** Warns once per `owner`, and never in production. */\nexport function warnOnce(owner: object, message: string): void {\n if (isProduction() || warnedOwners.has(owner)) {\n return\n }\n\n warnedOwners.add(owner)\n console.warn(message)\n}\n","import { type ComponentType, type ReactNode, use } from 'react'\n\nimport { declaresEmptyValue } from '../widget/declares-empty-value'\n\nimport { ArrayEntry } from './array-entry'\nimport { byName } from './by-name'\nimport { childPath } from './child-path'\nimport { Derived } from './derived'\nimport { pathPattern } from './path-pattern'\nimport type { AppFieldProps, RenderForm } from './render-form'\nimport { NO_REQUIRED_PATHS, RequiredPaths } from './required-paths'\nimport { splitEntry } from './split-entry'\nimport { warnOnce } from './warn-once'\n\n// biome-ignore lint/suspicious/noExplicitAny: registered components take props of their own; the config types check them.\ntype AnyProps = any\n\ntype ComponentMap = Readonly<Record<string, ComponentType<AnyProps>>>\n\n/** The maps of a registry the renderer looks names up in. */\ntype RenderParts = {\n widgets: ComponentMap\n formComponents: ComponentMap\n structure: {\n section?: ComponentType<AnyProps>\n row?: ComponentType<AnyProps>\n arrayFrame?: ComponentType<AnyProps>\n }\n}\n\ntype LevelFields = Readonly<Record<string, object>>\n\ntype LayoutNodeEntry = string | Readonly<Record<string, unknown>>\n\n/** The props of {@link Fields}. */\nexport type FieldsProps = {\n parts: RenderParts\n form: RenderForm\n fields: LevelFields\n layout?: readonly unknown[] | undefined\n /** The path patterns of the required fields; none unless `createForm` renders the level. */\n requiredPaths?: ReadonlySet<string> | undefined\n}\n\ntype Level = Omit<FieldsProps, 'layout'> & {\n path: string | undefined\n /** Props an array frame passes the one widget of an item, through this level. */\n extra?: object | undefined\n}\n\ntype LevelProps = Level & {\n layout: readonly unknown[] | undefined\n}\n\ntype EntryProps = Omit<Level, 'fields'> & {\n entry: object\n path: string\n}\n\ntype FieldContentProps = Omit<EntryProps, 'entry'> & {\n entry: ReturnType<typeof splitEntry> & { source: object }\n props: object | undefined\n}\n\ntype ItemProps = Omit<EntryProps, 'extra'> & {\n index: number\n extra: object | undefined\n}\n\ntype LayoutNodeProps = {\n level: Level\n entry: LayoutNodeEntry\n}\n\ntype LayoutKindProps = {\n level: Level\n entry: Readonly<Record<string, unknown>>\n props: object | undefined\n}\n\nconst ARRAY_MODE = 'array'\n\nconst DROPPED_EXTRA = 'passes extra props to an item that has no single widget to receive them'\n\nconst renderNothing = (): ReactNode => null\n\nconst layoutOwner = (path: string | undefined): string =>\n path === undefined ? 'the form' : `\"${path}\"`\n\nconst isLeaf = (entry: unknown): boolean =>\n typeof entry === 'object' && entry !== null && 'widget' in entry\n\n// A frame's extra props have a single target only in a leaf, or an object level whose one field\n// is a leaf and which has no layout.\nconst takesExtra = (item: object): boolean => {\n if (isLeaf(item)) {\n return true\n }\n const { own } = splitEntry(item)\n const fields =\n typeof own.fields === 'object' && own.fields !== null ? Object.values(own.fields) : []\n\n return own.layout === undefined && fields.length === 1 && isLeaf(fields[0])\n}\n\nconst Item = ({ parts, form, entry, path, index, extra }: ItemProps): ReactNode => {\n const { own } = splitEntry(entry)\n const item = own.item as object\n const reachesWidget = extra !== undefined && takesExtra(item)\n if (extra !== undefined && !reachesWidget) {\n warnOnce(entry, `formdef: the array frame of \"${path}\" ${DROPPED_EXTRA}; they are dropped.`)\n }\n\n return (\n <Entry\n parts={parts}\n form={form}\n entry={item}\n path={childPath(path, index)}\n extra={reachesWidget ? extra : undefined}\n />\n )\n}\n\nconst FieldContent = ({ parts, form, entry, path, props, extra }: FieldContentProps): ReactNode => {\n const { own, fieldOptions } = entry\n const requiredPaths = use(RequiredPaths)\n\n if (typeof own.widget === 'string') {\n const Widget = byName(parts.widgets, own.widget)\n if (Widget === undefined) {\n throw new Error(`formdef: field \"${path}\" names the unregistered widget \"${own.widget}\".`)\n }\n\n // The config's `required` comes after the found one, so it wins.\n const found = declaresEmptyValue(Widget)\n ? { required: requiredPaths.has(pathPattern(path)) }\n : undefined\n\n return (\n <form.AppField {...fieldOptions} name={path}>\n {(field) => <Widget {...found} {...props} {...extra} field={field} />}\n </form.AppField>\n )\n }\n\n if (typeof own.render === 'function') {\n return (\n <form.AppField {...fieldOptions} name={path}>\n {own.render as AppFieldProps['children']}\n </form.AppField>\n )\n }\n\n if (typeof own.item === 'object' && own.item !== null) {\n return (\n <form.AppField {...fieldOptions} name={path} mode={ARRAY_MODE}>\n {(field) => (\n <ArrayEntry\n field={field}\n options={own}\n frame={parts.structure.arrayFrame}\n props={props}\n renderItem={(index, itemExtra) => (\n <Item\n parts={parts}\n form={form}\n entry={entry.source}\n path={path}\n index={index}\n extra={itemExtra}\n />\n )}\n />\n )}\n </form.AppField>\n )\n }\n\n if (typeof own.fields !== 'object' || own.fields === null) {\n return null\n }\n\n return (\n <>\n {Object.keys(fieldOptions).length > 0 && (\n <form.AppField {...fieldOptions} name={path}>\n {renderNothing}\n </form.AppField>\n )}\n <LevelEntries\n parts={parts}\n form={form}\n fields={own.fields as LevelFields}\n layout={Array.isArray(own.layout) ? own.layout : undefined}\n path={path}\n extra={extra}\n />\n </>\n )\n}\n\nconst Entry = ({ parts, form, entry, path, extra }: EntryProps): ReactNode => {\n const split = { ...splitEntry(entry), source: entry }\n\n return (\n <Derived form={form} entry={split.own}>\n {(props) => (\n <FieldContent\n parts={parts}\n form={form}\n entry={split}\n path={path}\n props={props}\n extra={extra}\n />\n )}\n </Derived>\n )\n}\n\nconst FieldNode = ({ level, name }: { level: Level; name: string }): ReactNode => {\n const entry = byName(level.fields, name)\n if (entry === undefined) {\n throw new Error(\n `formdef: the layout of ${layoutOwner(level.path)} names \"${name}\", which the level does not describe.`,\n )\n }\n\n return (\n <Entry\n parts={level.parts}\n form={level.form}\n entry={entry}\n path={childPath(level.path, name)}\n extra={level.extra}\n />\n )\n}\n\nconst LayoutNodes = ({ level, entries }: { level: Level; entries: unknown }): ReactNode =>\n (Array.isArray(entries) ? (entries as LayoutNodeEntry[]) : []).map((entry, index) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: a layout comes from the config and never reorders; a name may appear twice.\n <LayoutNode key={index} level={level} entry={entry} />\n ))\n\nconst SectionNode = ({ level, entry, props }: LayoutKindProps): ReactNode => {\n const children = <LayoutNodes level={level} entries={entry.layout} />\n const Section = level.parts.structure.section\n\n if (Section !== undefined) {\n return (\n <Section {...props} label={entry.section}>\n {children}\n </Section>\n )\n }\n\n return (\n <fieldset {...props}>\n <legend>{entry.section as string}</legend>\n {children}\n </fieldset>\n )\n}\n\nconst RowNode = ({ level, entry, props }: LayoutKindProps): ReactNode => {\n const children = <LayoutNodes level={level} entries={entry.row} />\n const Row = level.parts.structure.row\n\n if (Row !== undefined) {\n return <Row {...props}>{children}</Row>\n }\n\n return (\n <div {...props} data-formdef-row=\"\">\n {children}\n </div>\n )\n}\n\nconst ComponentNode = ({ level, entry, props }: LayoutKindProps): ReactNode => {\n const name = entry.component as string\n const Component = byName(level.parts.formComponents, name)\n if (Component === undefined) {\n throw new Error(\n `formdef: the layout of ${layoutOwner(level.path)} places the unregistered form component \"${name}\".`,\n )\n }\n\n return <Component {...props} />\n}\n\nconst LayoutKind = ({ level, entry, props }: LayoutKindProps): ReactNode => {\n if ('section' in entry) {\n return <SectionNode level={level} entry={entry} props={props} />\n }\n if ('row' in entry) {\n return <RowNode level={level} entry={entry} props={props} />\n }\n if (typeof entry.component === 'string') {\n return <ComponentNode level={level} entry={entry} props={props} />\n }\n if (typeof entry.render === 'function') {\n return (entry.render as (form: RenderForm) => ReactNode)(level.form)\n }\n\n return null\n}\n\nconst LayoutNode = ({ level, entry }: LayoutNodeProps): ReactNode => {\n if (typeof entry === 'string') {\n return <FieldNode level={level} name={entry} />\n }\n\n return (\n <Derived form={level.form} entry={entry}>\n {(props) => <LayoutKind level={level} entry={entry} props={props} />}\n </Derived>\n )\n}\n\nconst LevelEntries = ({ layout, ...level }: LevelProps): ReactNode => {\n if (layout !== undefined) {\n return <LayoutNodes level={level} entries={layout} />\n }\n\n return Object.keys(level.fields).map((name) => <FieldNode key={name} level={level} name={name} />)\n}\n\n/**\n * Renders the field entries of a level with the widgets of `parts`. Without a `layout` the entries\n * appear in the order `fields` declares them; with one, the layout decides the order, groups them\n * into sections and rows and places form components and inline entries. A leaf renders as its\n * widget, an object as its own level, a custom entry through its `render`, an array through the\n * registered `structure.arrayFrame` or the bare default. An object entry with TanStack options of\n * its own also mounts a field at its path, so its validators run. A `Fields` nested in a form finds\n * no required fields of its own, since its paths may be relative to a field group.\n */\nexport const Fields = ({ parts, form, fields, layout, requiredPaths }: FieldsProps): ReactNode => (\n <RequiredPaths value={requiredPaths ?? NO_REQUIRED_PATHS}>\n <LevelEntries parts={parts} form={form} fields={fields} layout={layout} path={undefined} />\n </RequiredPaths>\n)\n","import type { StandardSchemaV1 } from '@tanstack/react-form'\n\nimport { byName } from '../render/by-name'\nimport { childPath } from '../render/child-path'\nimport { pathPattern } from '../render/path-pattern'\nimport { splitEntry } from '../render/split-entry'\nimport { declaresEmptyValue } from '../widget/declares-empty-value'\n\ntype LevelFields = Readonly<Record<string, object>>\n\ntype Widgets = Readonly<Record<string, object>>\n\ntype RequiredPathsInput = {\n schema: unknown\n /** Whether the config validates the schema asynchronously (`validation.async`). */\n validatesAsync: boolean\n fields: LevelFields\n widgets: Widgets\n}\n\n/** The path patterns of the required fields, or why none could be found. */\ntype RequiredPathsResult = { paths: ReadonlySet<string> } | { problem: string }\n\ntype EmptyValues = {\n widgets: Widgets\n /** The path patterns of the leaves whose widget declares an empty value. */\n declared: Set<string>\n}\n\ntype Validation = ReturnType<StandardSchemaV1['~standard']['validate']>\ntype Issue = NonNullable<Exclude<Validation, Promise<unknown>>['issues']>[number]\n\nconst NONE: RequiredPathsResult = { paths: new Set() }\n\nconst ASYNC_SCHEMA =\n 'formdef: the schema validates asynchronously, so no field is found to be required; pass required in props.'\nconst UNSETTLED_SCHEMA =\n 'formdef: the schema did not validate empty values synchronously, so no field is found to be required; pass required in props.'\nconst THROWING_SCHEMA =\n 'formdef: the schema threw on empty values, so no field is found to be required'\n\nconst FIRST_ITEM = 0\n\nconst isStandardSchema = (schema: unknown): schema is StandardSchemaV1 =>\n typeof schema === 'object' && schema !== null && '~standard' in schema\n\nconst emptyLevel = (fields: LevelFields, path: string | undefined, empty: EmptyValues): object =>\n Object.fromEntries(\n Object.entries(fields).map(([key, entry]) => [\n key,\n emptyEntry(entry, childPath(path, key), empty),\n ]),\n )\n\nfunction emptyEntry(entry: object, path: string, empty: EmptyValues): unknown {\n const { own } = splitEntry(entry)\n\n if (typeof own.widget === 'string') {\n const widget = byName(empty.widgets, own.widget)\n if (widget === undefined || !declaresEmptyValue(widget)) {\n return undefined\n }\n empty.declared.add(pathPattern(path))\n\n return widget.emptyValue\n }\n if (typeof own.item === 'object' && own.item !== null) {\n return [emptyEntry(own.item, childPath(path, FIRST_ITEM), empty)]\n }\n if (typeof own.fields === 'object' && own.fields !== null) {\n return emptyLevel(own.fields as LevelFields, path, empty)\n }\n\n return undefined\n}\n\nconst issuePath = (issue: Issue): string =>\n pathPattern(\n (issue.path ?? []).reduce<string | undefined>((parent, segment) => {\n const key = typeof segment === 'object' ? segment.key : segment\n\n return childPath(parent, typeof key === 'number' ? key : String(key))\n }, undefined) ?? '',\n )\n\nconst parentOf = (path: string): string =>\n path.slice(0, Math.max(path.lastIndexOf('.'), path.lastIndexOf('[')))\n\n// A leaf's value may be an object or a list, so an issue below a leaf's path is the leaf's.\nconst leafOf = (path: string, leaves: ReadonlySet<string>): string | undefined => {\n for (let candidate = path; candidate !== ''; candidate = parentOf(candidate)) {\n if (leaves.has(candidate)) {\n return candidate\n }\n }\n\n return undefined\n}\n\n// The promise is dropped unread; a rejection is the schema failing on empty values, not the form.\nconst ignoreRejection = (): undefined => undefined\n\nconst errorText = (error: unknown): string =>\n error instanceof Error ? error.message : String(error)\n\n/**\n * Finds the required fields of a config: validates `schema` once, synchronously, with every leaf\n * holding its widget's empty value and every array one such item. A leaf whose widget declares an\n * empty value and whose path receives an issue is required. A schema the config validates\n * asynchronously is not run, so its asynchronous checks send nothing.\n */\nexport function findRequiredPaths({\n schema,\n validatesAsync,\n fields,\n widgets,\n}: RequiredPathsInput): RequiredPathsResult {\n if (!isStandardSchema(schema)) {\n return NONE\n }\n if (validatesAsync) {\n return { problem: ASYNC_SCHEMA }\n }\n\n const empty: EmptyValues = { widgets, declared: new Set() }\n const values = emptyLevel(fields, undefined, empty)\n let result: Validation\n try {\n result = schema['~standard'].validate(values)\n } catch (error) {\n return { problem: `${THROWING_SCHEMA}: ${errorText(error)}` }\n }\n if (result instanceof Promise) {\n result.then(undefined, ignoreRejection)\n\n return { problem: UNSETTLED_SCHEMA }\n }\n\n const leaves = (result.issues ?? []).map((issue) => leafOf(issuePath(issue), empty.declared))\n\n return { paths: new Set(leaves.filter((leaf) => leaf !== undefined)) }\n}\n","import {\n defaultValidationLogic,\n type ValidationLogicFn,\n type ValidationLogicProps,\n} from '@tanstack/react-form'\n\ntype ScheduledValidators = Parameters<ValidationLogicProps['runValidation']>[0]['validators']\n\n// Gated on the form, not the changed field: the schema validates the whole form, and TanStack names\n// the changed field only on the field-level call, where the schema does not sit.\nconst isSchemaDue = ({ event, form }: ValidationLogicProps): boolean => {\n if (event.type === 'blur' || event.type === 'submit') {\n return true\n }\n\n return event.type === 'change' && !form.state.isValid\n}\n\n/**\n * The validation logic of the `blurThenChange` mode: TanStack's default logic, plus the schema in\n * `onDynamic` when a field is left, on submit, and on every change while the form is invalid.\n */\nexport const blurThenChange: ValidationLogicFn = (props) => {\n let defaults: ScheduledValidators = []\n defaultValidationLogic({\n ...props,\n runValidation: ({ validators }) => {\n defaults = validators\n },\n })\n\n const schema = props.event.async ? props.validators?.onDynamicAsync : props.validators?.onDynamic\n if (schema === undefined || !isSchemaDue(props)) {\n return props.runValidation({ validators: defaults, form: props.form })\n }\n\n return props.runValidation({\n validators: [...defaults, { fn: schema, cause: 'dynamic' }],\n form: props.form,\n })\n}\n","import { revalidateLogic, type ValidationLogicFn } from '@tanstack/react-form'\n\nimport type { Validation } from '../config/validation'\nimport { blurThenChange } from './blur-then-change'\n\ntype Mode = NonNullable<Validation['mode']>\ntype DynamicMode = 'revalidate' | 'blurThenChange'\ntype Slot = 'onDynamic' | 'onChange' | 'onBlur' | 'onSubmit'\n\ntype Options = Readonly<Record<string, unknown>>\n\ntype FormOptionsInput = {\n /** The form config: the schema, formdef's own keys and TanStack's form options. */\n config: Options\n /** The instance's TanStack form options, from the form component's `options` prop. */\n options: Options | undefined\n}\n\nconst DEFAULT_MODE: Mode = 'revalidate'\n\nconst isDynamic = (mode: Mode): mode is DynamicMode =>\n mode === 'revalidate' || mode === 'blurThenChange'\n\nconst logicFor = (mode: DynamicMode): ValidationLogicFn =>\n mode === 'revalidate' ? revalidateLogic() : blurThenChange\n\nconst slotFor = (mode: Mode, isAsync: boolean): Slot | `${Slot}Async` => {\n const slot: Slot = isDynamic(mode) ? 'onDynamic' : mode\n\n return isAsync ? `${slot}Async` : slot\n}\n\nconst recordOf = (value: unknown): Options =>\n typeof value === 'object' && value !== null ? (value as Options) : {}\n\n// An instance passing `undefined`, such as data still loading, leaves the config's value in place.\nconst setEntriesOf = (value: unknown): Options =>\n Object.fromEntries(Object.entries(recordOf(value)).filter(([, entry]) => entry !== undefined))\n\n/**\n * The TanStack form options of a form: the config's options and the instance's merged key by key,\n * the instance winning, except `validators` and `listeners`, which merge slot by slot; a key or\n * slot the instance sets to `undefined` counts as unset. The schema\n * goes into the slot `validation` picks unless a validator already fills it; a dynamic mode brings\n * its validation logic, an asynchronous schema its debounce unless the options set one.\n */\nexport function formOptionsFor({ config, options }: FormOptionsInput): Record<string, unknown> {\n const {\n schema,\n validation,\n fields: _renderedByFields,\n layout: _arrangedByFields,\n ...configOptions\n } = config\n const { mode = DEFAULT_MODE, ...timing } = recordOf(validation) as Validation\n const isAsync = timing.async === true\n const slot = slotFor(mode, isAsync)\n const instanceOptions = setEntriesOf(options)\n const validators = {\n ...recordOf(configOptions.validators),\n ...setEntriesOf(instanceOptions.validators),\n }\n\n return {\n ...(isDynamic(mode) && { validationLogic: logicFor(mode) }),\n ...(isAsync &&\n timing.asyncDebounceMs !== undefined && { asyncDebounceMs: timing.asyncDebounceMs }),\n ...configOptions,\n ...instanceOptions,\n validators: validators[slot] === undefined ? { ...validators, [slot]: schema } : validators,\n listeners: { ...recordOf(configOptions.listeners), ...setEntriesOf(instanceOptions.listeners) },\n }\n}\n","import type { ComponentType, FormEvent, ReactNode } from 'react'\n\nimport type { FormElementProps } from '../config/form-props'\nimport type { BodyProps } from '../config/structure'\nimport { Fields, type FieldsProps } from '../render/fields'\nimport type { RenderForm } from '../render/render-form'\nimport { warnOnce } from '../render/warn-once'\nimport { findRequiredPaths } from './find-required-paths'\nimport { formOptionsFor } from './form-options-for'\n\ntype Options = Readonly<Record<string, unknown>>\n\n/** The form `useAppForm` returns, as far as the shell uses it. */\ntype ShellForm = RenderForm & {\n AppForm: ComponentType<{ children?: ReactNode }>\n handleSubmit: () => Promise<void>\n}\n\n/** What a registry binds a form to: its form hook and the maps the renderer looks names up in. */\nexport type FormBinding = {\n useAppForm: (options: Options) => ShellForm\n parts: FieldsProps['parts'] & {\n structure: { body?: ComponentType<BodyProps> }\n }\n}\n\n/** The props of a form component: the instance's options and what a `<form>` element takes. */\ntype FormComponentProps = FormElementProps & {\n options?: Options\n}\n\n/** A form config: TanStack's form options plus formdef's own keys. */\ntype ConfigInput = Options & {\n fields?: unknown\n layout?: unknown\n}\n\ntype ConfiguredFormProps = FormComponentProps & {\n binding: FormBinding\n config: ConfigInput\n requiredPaths: ReadonlySet<string>\n}\n\nconst MISSING_DEFAULT_VALUES =\n 'formdef: the form has no default values; set defaultValues in its config or in its options.'\n\nconst ConfiguredForm = ({\n binding,\n config,\n requiredPaths,\n options,\n ...formProps\n}: ConfiguredFormProps): ReactNode => {\n const formOptions = formOptionsFor({ config, options })\n if (formOptions.defaultValues === undefined) {\n throw new Error(MISSING_DEFAULT_VALUES)\n }\n\n const form = binding.useAppForm(formOptions)\n const Body = binding.parts.structure.body\n const content = (\n <Fields\n parts={binding.parts}\n form={form}\n fields={config.fields as FieldsProps['fields']}\n layout={config.layout as FieldsProps['layout']}\n requiredPaths={requiredPaths}\n />\n )\n const handleSubmit = (event: FormEvent<HTMLFormElement>): void => {\n event.preventDefault()\n event.stopPropagation()\n void form.handleSubmit()\n }\n\n return (\n <form.AppForm>\n <form noValidate {...formProps} onSubmit={handleSubmit}>\n {Body === undefined ? content : <Body>{content}</Body>}\n </form>\n </form.AppForm>\n )\n}\n\nconst isAsyncValidation = (validation: unknown): boolean =>\n typeof validation === 'object' && validation !== null && 'async' in validation\n ? validation.async === true\n : false\n\nconst requiredPathsOf = (binding: FormBinding, config: ConfigInput): ReadonlySet<string> => {\n const result = findRequiredPaths({\n schema: config.schema,\n validatesAsync: isAsyncValidation(config.validation),\n fields: (config.fields ?? {}) as Readonly<Record<string, object>>,\n widgets: binding.parts.widgets,\n })\n if ('problem' in result) {\n warnOnce(config, result.problem)\n\n return new Set()\n }\n\n return result.paths\n}\n\n/**\n * Turns a form config into a form component bound to `binding`: TanStack's form with the merged\n * options, a `<form>` element that submits through it, the registered body and the config's fields.\n * When it first renders, it finds the config's required fields once.\n */\nexport function createForm(\n binding: FormBinding,\n config: ConfigInput,\n): (props: FormComponentProps) => ReactNode {\n let requiredPaths: ReadonlySet<string> | undefined\n\n return (props: FormComponentProps): ReactNode => {\n requiredPaths ??= requiredPathsOf(binding, config)\n\n return (\n <ConfiguredForm {...props} binding={binding} config={config} requiredPaths={requiredPaths} />\n )\n }\n}\n","import { type AnyFieldApi, type AnyFormApi, createFormHook } from '@tanstack/react-form'\nimport { type Context, createElement, type ReactNode } from 'react'\n\nimport { createForm, type FormBinding } from '../form/create-form'\nimport { Fields, type FieldsProps } from '../render/fields'\n\nimport type {\n CheckedStructure,\n CheckedWidgets,\n Components,\n NewFormComponents,\n NewStructure,\n NewWidgets,\n Registry,\n Structure,\n} from './registry'\n\ntype Contexts = {\n fieldContext: Context<AnyFieldApi>\n formContext: Context<AnyFormApi>\n}\n\ntype RegistryParts<TWidgets, TFormComponents, TStructure> = Contexts & {\n widgets: TWidgets\n formComponents: TFormComponents\n structure: TStructure\n}\n\nfunction buildRegistry<\n TWidgets extends Components,\n TFormComponents extends Components,\n TStructure extends Structure,\n>(\n parts: RegistryParts<TWidgets, TFormComponents, TStructure>,\n): Registry<TWidgets, TFormComponents, TStructure> {\n const { extendForm: _replacedByExtendRegistry, ...formHook } = createFormHook({\n fieldContext: parts.fieldContext,\n formContext: parts.formContext,\n fieldComponents: parts.widgets,\n formComponents: parts.formComponents,\n })\n\n function extendRegistry<\n const TNewWidgets extends NewWidgets<TWidgets> = Record<never, never>,\n const TNewFormComponents extends NewFormComponents<TFormComponents> = Record<never, never>,\n const TNewStructure extends NewStructure<TStructure> = Record<never, never>,\n >(extension: {\n widgets?: TNewWidgets\n formComponents?: TNewFormComponents\n structure?: TNewStructure & CheckedStructure<TNewStructure>\n }): Registry<\n TWidgets & TNewWidgets,\n TFormComponents & TNewFormComponents,\n TStructure & TNewStructure\n > {\n const addedWidgets: Components = extension.widgets ?? {}\n const addedStructure: Structure = extension.structure ?? {}\n\n // The extension's maps are optional, and spreading an optional generic map drops its type.\n return buildRegistry({\n fieldContext: parts.fieldContext,\n formContext: parts.formContext,\n widgets: { ...parts.widgets, ...addedWidgets } as TWidgets & TNewWidgets,\n formComponents: { ...parts.formComponents, ...extension.formComponents } as TFormComponents &\n TNewFormComponents,\n structure: { ...parts.structure, ...addedStructure } as TStructure & TNewStructure,\n })\n }\n\n // TanStack's hook is generic over the form's types; the config's types are checked at the\n // registry's `createForm`, so the binding passes the merged options on untyped.\n const binding: FormBinding = {\n useAppForm: formHook.useAppForm as unknown as FormBinding['useAppForm'],\n parts: {\n widgets: parts.widgets,\n formComponents: parts.formComponents,\n structure: parts.structure,\n },\n }\n\n // biome-ignore lint/correctness/noComponentHookFactories: created once per registry, like TanStack's createFormHook.\n const BoundFields = (props: Omit<FieldsProps, 'parts'>): ReactNode =>\n createElement(Fields, { ...props, parts: binding.parts })\n\n return {\n formHook,\n createForm: (config) => createForm(binding, config),\n // The member's signature types the level by the form it is given; the renderer reads it untyped.\n Fields: BoundFields as unknown as Registry<TWidgets, TFormComponents, TStructure>['Fields'],\n // The member's signature checks the widgets' empty values; the implementation merges any map.\n extendRegistry: extendRegistry as Registry<\n TWidgets,\n TFormComponents,\n TStructure\n >['extendRegistry'],\n }\n}\n\n/**\n * Binds widgets, form components and structure slots to the app's TanStack contexts and returns a\n * registry, the value every form of the app is built with.\n *\n * Create the contexts with TanStack's `createFormHookContexts()` in a module of their own, so a\n * form component can read the form through `useFormContext()` without importing the registry.\n *\n * @typeParam TWidgets - The widgets by name; each takes its field as a prop typed `FieldOf<V>`.\n * @typeParam TFormComponents - The form components by name, placed in a layout by `{ component }`.\n * @typeParam TStructure - The structure slots: `body`, `section`, `row` and `arrayFrame`.\n * @param config - The contexts and what the registry holds.\n * @param config.fieldContext - The field context from `createFormHookContexts()`.\n * @param config.formContext - The form context from `createFormHookContexts()`.\n * @param config.widgets - The widgets by name. A widget's static `emptyValue`, the value it holds\n * while nothing is entered, must be of the value type it serves.\n * @param config.formComponents - The form components by name.\n * @param config.structure - The components that render the structure slots.\n * @defaultValue `config.formComponents` and `config.structure`: none registered.\n * @returns The registry.\n *\n * @example\n * ```tsx\n * 'use client'\n * import { createRegistry } from '@formdef/core'\n * import { fieldContext, formContext } from './context'\n *\n * export const registry = createRegistry({\n * fieldContext,\n * formContext,\n * widgets: { text: TextField, checkbox: CheckboxField },\n * formComponents: { SubmitButton },\n * structure: { section: Section },\n * })\n * ```\n */\nexport function createRegistry<\n const TWidgets extends Components,\n const TFormComponents extends Components = Record<never, never>,\n const TStructure extends Structure = Record<never, never>,\n>(\n config: Contexts & {\n widgets: TWidgets & CheckedWidgets<TWidgets>\n formComponents?: TFormComponents\n structure?: TStructure & CheckedStructure<TStructure>\n },\n): Registry<TWidgets, TFormComponents, TStructure> {\n // Both maps fall back to their empty defaults exactly when they are left out.\n return buildRegistry<TWidgets, TFormComponents, TStructure>({\n fieldContext: config.fieldContext,\n formContext: config.formContext,\n widgets: config.widgets,\n formComponents: config.formComponents ?? ({} as TFormComponents),\n structure: config.structure ?? ({} as TStructure),\n })\n}\n","import type { FieldApiOf } from './field-of'\n\nfunction textOf(error: unknown): string | undefined {\n if (typeof error === 'string') {\n return error\n }\n if (typeof error !== 'object' || error === null || !('message' in error)) {\n return undefined\n }\n\n return typeof error.message === 'string' ? error.message : undefined\n}\n\nfunction hasText(text: string | undefined): text is string {\n return text !== undefined && text !== ''\n}\n\n/**\n * Turns the errors of a field into the texts a widget renders.\n *\n * An error is either a string or an object with a `message`, such as an issue of a Standard\n * Schema. Errors without text are left out, and a text that several validators report appears\n * once, where it first occurs. A field's errors stay readable with TanStack's `disableErrorFlat`\n * option.\n *\n * @typeParam TValue - The value type of the field.\n * @param field - The field the widget renders.\n * @returns The distinct texts of the field's current errors, in order; empty when there are none.\n *\n * @example\n * ```tsx\n * const TextField = ({ field }: FieldOf<string>) => (\n * <>\n * <input value={field.state.value} onChange={(event) => field.handleChange(event.target.value)} />\n * <p>{errorsOf(field).join(' ')}</p>\n * </>\n * )\n * ```\n */\nexport function errorsOf<TValue>(field: FieldApiOf<TValue>): string[] {\n const texts = field.state.meta.errors.flat().map(textOf).filter(hasText)\n\n return [...new Set(texts)]\n}\n","const PATH_SEPARATORS = /[.[\\]]+/g\nconst SELECTOR_SAFE_SEPARATOR = '__'\n\n/**\n * Builds the DOM id that `fieldParts` gives the control of a field, for code that has no\n * field at hand — an error summary above the form that links to each invalid field, for example.\n *\n * The id is the form's id, a hyphen, and the field's path with every run of dots and brackets\n * replaced by `__`. Fields of two different forms therefore never share an id, and the id can be\n * used in a CSS selector without escaping.\n *\n * @param formId - The id of the form, as TanStack Form reports it in `form.formId`.\n * @param path - The path of the field, such as `email`, `address.zip` or `contacts[0].name`.\n * @returns The id of the field's control, such as `signup-address__zip`.\n *\n * @remarks\n * The replacement cannot be reversed: a field literally named `a__b` gets the same id as the\n * path `a.b`.\n *\n * @example\n * ```tsx\n * <a href={`#${fieldId(form.formId, 'address.zip')}`}>Postal code</a>\n * ```\n */\nexport function fieldId(formId: string, path: string): string {\n return `${formId}-${path.replace(PATH_SEPARATORS, SELECTOR_SAFE_SEPARATOR)}`\n}\n","import { errorsOf } from './errors-of'\nimport { fieldId } from './field-id'\nimport type { FieldApiOf } from './field-of'\n\nconst DESCRIPTION_SUFFIX = '-description'\nconst ERROR_SUFFIX = '-error'\nconst IDREF_SEPARATOR = ' '\n\n/**\n * What {@link fieldParts} returns: the wiring of a widget's control and what it needs to render\n * its description and its errors.\n */\nexport interface FieldParts {\n /** Props to spread onto the control; its value and change handler stay with the widget. */\n control: {\n /** The control's id, as {@link fieldId} builds it. */\n id: string\n /** The field's path, so the browser can autofill the control. */\n name: string\n /** Reports to the field that the control was left. */\n onBlur: () => void\n /** `true` while an error is due; absent otherwise, never `false`. */\n 'aria-invalid': true | undefined\n /** The ids of the description and of a due error with text, when there are any. */\n 'aria-describedby': string | undefined\n /** The id of the error element, while an error with text is due. */\n 'aria-errormessage': string | undefined\n }\n /** The id to give the element that renders the description. */\n descriptionId: string\n /** The id to give the element that renders the error texts. */\n errorId: string\n /**\n * Whether the widget shows the field's errors now: the field is invalid and was edited and\n * left, or the form was submitted.\n */\n isErrorDue: boolean\n /** The texts of the field's errors, as {@link errorsOf} returns them. */\n errors: string[]\n}\n\nfunction isErrorDueFor<TValue>(field: FieldApiOf<TValue>): boolean {\n const { isBlurred, isDirty, isValid } = field.state.meta\n const wasEditedAndLeft = isBlurred && isDirty\n const wasSubmitted = field.form.state.submissionAttempts > 0\n\n return !isValid && (wasEditedAndLeft || wasSubmitted)\n}\n\nfunction idrefsOf(ids: (string | undefined)[]): string | undefined {\n const present = ids.filter((id) => id !== undefined)\n\n return present.length > 0 ? present.join(IDREF_SEPARATOR) : undefined\n}\n\n/**\n * Wires a widget's control to its field: its id and name, the blur report, and the aria\n * attributes that tie it to its description and its errors.\n *\n * An error is due once the field is invalid and was edited and left, or once the form was\n * submitted. While one is due, `aria-invalid` is `true`; while one with text is due, the control\n * points at the error element through `aria-describedby` and `aria-errormessage`. An id appears in\n * them only when its element is rendered.\n *\n * @typeParam TValue - The value type of the field.\n * @param field - The field the widget renders.\n * @param options - What the widget renders besides the control.\n * @param options.description - The description the widget renders below its label, if any. Only\n * whether it is a non-empty string is read.\n * @defaultValue `options.description`: `undefined`, no description is rendered.\n * @returns The props for the control, the ids for the description and error elements, whether an\n * error is due, and the error texts.\n *\n * @example\n * ```tsx\n * const TextField = ({ field, label, description }: FieldOf<string> & { label: string; description?: string }) => {\n * const { control, descriptionId, errorId, isErrorDue, errors } = fieldParts(field, { description })\n *\n * return (\n * <div>\n * <label htmlFor={control.id}>{label}</label>\n * {description && <p id={descriptionId}>{description}</p>}\n * <input {...control} value={field.state.value} onChange={(event) => field.handleChange(event.target.value)} />\n * {isErrorDue && errors.length > 0 && <p id={errorId}>{errors.join(' ')}</p>}\n * </div>\n * )\n * }\n * ```\n */\nexport function fieldParts<TValue>(\n field: FieldApiOf<TValue>,\n options: { description?: string | undefined } = {},\n): FieldParts {\n const id = fieldId(field.form.formId, field.name)\n const descriptionId = `${id}${DESCRIPTION_SUFFIX}`\n const errorId = `${id}${ERROR_SUFFIX}`\n const isErrorDue = isErrorDueFor(field)\n const errors = errorsOf(field)\n const isErrorNamed = isErrorDue && errors.length > 0\n const hasDescription = options.description !== undefined && options.description !== ''\n\n return {\n control: {\n id,\n name: field.name,\n onBlur: field.handleBlur,\n 'aria-invalid': isErrorDue || undefined,\n 'aria-describedby': idrefsOf([\n hasDescription ? descriptionId : undefined,\n isErrorNamed ? errorId : undefined,\n ]),\n 'aria-errormessage': isErrorNamed ? errorId : undefined,\n },\n descriptionId,\n errorId,\n isErrorDue,\n errors,\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["useSelector","Fragment","useRef","getBy","createContext","use","revalidateLogic","createFormHook","createElement","useSelector"],"sources":["../src/widget/declares-empty-value.ts","../src/render/array-entry.tsx","../src/render/by-name.ts","../src/render/child-path.ts","../src/render/derived.tsx","../src/render/path-pattern.ts","../src/render/required-paths.ts","../src/render/split-entry.ts","../src/render/warn-once.ts","../src/render/fields.tsx","../src/form/find-required-paths.ts","../src/form/blur-then-change.ts","../src/form/form-options-for.ts","../src/form/create-form.tsx","../src/registry/create-registry.ts","../src/widget/errors-of.ts","../src/widget/field-id.ts","../src/widget/field-parts.ts","../src/widget/use-field-parts.ts"],"sourcesContent":["/** Whether `widget` declares its empty value, the value it holds while nothing is entered. */\nexport function declaresEmptyValue(widget: object): widget is { emptyValue: unknown } {\n return Object.hasOwn(widget, 'emptyValue')\n}\n","import { type AnyFieldApi, useSelector } from '@tanstack/react-form'\nimport { type ComponentType, Fragment, type ReactNode } from 'react'\n\n// biome-ignore lint/suspicious/noExplicitAny: a registered frame takes props of its own; the config types check them.\ntype AnyProps = any\n\n/** The keys of an array entry the renderer reads besides its item. */\ntype ArrayOptions = {\n newItem?: unknown\n addLabel?: unknown\n removeLabel?: unknown\n itemKey?: unknown\n}\n\ntype ArrayEntryProps = {\n /** The array field, mounted at the entry's path. */\n field: AnyFieldApi\n options: ArrayOptions\n /** The registered `structure.arrayFrame`, if any. */\n frame: ComponentType<AnyProps> | undefined\n /** The entry's props for the frame. */\n props: object | undefined\n renderItem: (index: number, extra?: object) => ReactNode\n}\n\ntype ItemFunction<TResult> = (item: unknown, index?: number) => TResult\n\n/** What the controls of an array entry show: one key and one remove label per item. */\ntype ItemControls = {\n keys: (string | number)[]\n removeLabels: string[]\n}\n\nconst removeLabelOf = (removeLabel: unknown, item: unknown, index: number): string =>\n typeof removeLabel === 'function'\n ? (removeLabel as ItemFunction<string>)(item, index)\n : String(removeLabel)\n\nconst itemControlsOf = (options: ArrayOptions, value: unknown): ItemControls => {\n const items: unknown[] = Array.isArray(value) ? value : []\n\n return {\n keys: items.map((item, index) =>\n typeof options.itemKey === 'function'\n ? (options.itemKey as ItemFunction<string | number>)(item)\n : index,\n ),\n removeLabels: items.map((item, index) => removeLabelOf(options.removeLabel, item, index)),\n }\n}\n\nconst isSameList = <TItem,>(first: readonly TItem[], second: readonly TItem[]): boolean =>\n first.length === second.length && first.every((item, index) => item === second[index])\n\nconst isSameControls = (first: ItemControls, second: ItemControls): boolean =>\n isSameList(first.keys, second.keys) && isSameList(first.removeLabels, second.removeLabels)\n\n/**\n * Renders an array entry: through the registered frame, which receives `ArrayFrameProps`, or as\n * the bare default, each item followed by its remove button and one add button at the end.\n */\nexport const ArrayEntry = ({\n field,\n options,\n frame: Frame,\n props,\n renderItem,\n}: ArrayEntryProps): ReactNode => {\n // The field renders in TanStack's array mode, so only a change to the list reaches it; keys and\n // labels that name an item follow the items through a selection of their own.\n const { keys, removeLabels } = useSelector(\n field.store,\n (state: { value: unknown }) => itemControlsOf(options, state.value),\n { compare: isSameControls },\n )\n const newItem = options.newItem as () => unknown\n const addLabel = String(options.addLabel)\n const add = (): void => field.pushValue(newItem())\n const remove = (index: number): void => field.removeValue(index)\n\n if (Frame === undefined) {\n return (\n <>\n {keys.map((key, index) => (\n <Fragment key={key}>\n {renderItem(index)}\n <button type=\"button\" data-formdef-remove=\"\" onClick={() => remove(index)}>\n {removeLabels[index]}\n </button>\n </Fragment>\n ))}\n <button type=\"button\" data-formdef-add=\"\" onClick={add}>\n {addLabel}\n </button>\n </>\n )\n }\n\n return (\n <Frame\n {...props}\n field={field}\n keys={keys}\n renderItem={renderItem}\n addLabel={addLabel}\n removeLabels={removeLabels}\n add={add}\n remove={remove}\n insert={(index: number, item?: unknown) =>\n field.insertValue(index, item === undefined ? newItem() : item)\n }\n move={(from: number, to: number) => field.moveValue(from, to)}\n swap={(first: number, second: number) => field.swapValues(first, second)}\n replace={(index: number, item: unknown) => field.replaceValue(index, item)}\n clear={() => field.clearValues()}\n />\n )\n}\n","/** What `map` holds under `name` itself; a name every object inherits finds nothing. */\nexport function byName<TValue>(\n map: Readonly<Record<string, TValue>>,\n name: string,\n): TValue | undefined {\n return Object.hasOwn(map, name) ? map[name] : undefined\n}\n","/**\n * The path of `key` below `parent`: a key of the root level by itself, a key of a nested level\n * after a dot, the index of an array item in brackets.\n */\nexport function childPath(parent: string | undefined, key: string | number): string {\n if (typeof key === 'number') {\n return `${parent ?? ''}[${key}]`\n }\n\n return parent === undefined ? key : `${parent}.${key}`\n}\n","import { getBy } from '@tanstack/react-form'\nimport { type ReactNode, useRef } from 'react'\n\nimport type { RenderForm } from './render-form'\n\n/** What an entry reads from the form values: its condition, its props and the paths they read. */\ntype DerivedEntry = {\n visibleWhen?: unknown\n props?: unknown\n dependsOn?: unknown\n}\n\ntype DerivedProps = {\n form: RenderForm\n entry: DerivedEntry\n /** Builds the entry from its props; called only while the entry is visible. */\n children: (props: object | undefined) => ReactNode\n}\n\ntype LastRead = {\n entry: DerivedEntry\n dependencies: unknown[]\n selected: unknown\n}\n\nconst HIDDEN = Symbol('hidden')\nconst VISIBLE = Symbol('visible')\n\nconst propsOf = (props: unknown): object | undefined =>\n typeof props === 'object' && props !== null ? props : undefined\n\nconst isStale = (\n last: LastRead | undefined,\n entry: DerivedEntry,\n dependencies: unknown[],\n): boolean =>\n last === undefined ||\n last.entry !== entry ||\n dependencies.some((value, index) => !Object.is(value, last.dependencies[index]))\n\n/**\n * Renders an entry from what it reads from the form values. An entry with a condition or derived\n * props subscribes to the values; the selected result alone decides whether it re-renders: whether\n * it is shown for a condition, the values for derived props. `dependsOn` narrows when the selector\n * reads again, never what it returns. A hidden entry is never built.\n */\nexport const Derived = ({ form, entry, children }: DerivedProps): ReactNode => {\n const lastRead = useRef<LastRead | undefined>(undefined)\n const { visibleWhen, props, dependsOn } = entry\n const isConditional = typeof visibleWhen === 'function'\n const isDerived = typeof props === 'function'\n\n if (!(isConditional || isDerived)) {\n return children(propsOf(props))\n }\n\n const selectFrom = (values: unknown): unknown => {\n if (isConditional && !visibleWhen(values)) {\n return HIDDEN\n }\n\n return isDerived ? values : VISIBLE\n }\n\n const select = ({ values }: { values: unknown }): unknown => {\n if (!Array.isArray(dependsOn)) {\n return selectFrom(values)\n }\n\n const dependencies = dependsOn.map((path: string) => getBy(values, path))\n if (isStale(lastRead.current, entry, dependencies)) {\n lastRead.current = { entry, dependencies, selected: selectFrom(values) }\n }\n\n return lastRead.current?.selected\n }\n\n return (\n <form.Subscribe selector={select}>\n {(selected) => {\n if (selected === HIDDEN) {\n return null\n }\n\n return children(propsOf(isDerived ? props(selected) : props))\n }}\n </form.Subscribe>\n )\n}\n","const ITEM_INDEX = /\\[\\d+\\]/g\nconst ANY_ITEM = '[]'\n\n/** The path with every array index left open: `contacts[2].name` becomes `contacts[].name`. */\nexport function pathPattern(path: string): string {\n return path.replaceAll(ITEM_INDEX, ANY_ITEM)\n}\n","import { type Context, createContext } from 'react'\n\nexport const NO_REQUIRED_PATHS: ReadonlySet<string> = new Set()\n\n/** The path patterns of the required fields of the level being rendered. */\nexport const RequiredPaths: Context<ReadonlySet<string>> = createContext(NO_REQUIRED_PATHS)\n","/**\n * The keys formdef reads from a field entry. Every other key is one of TanStack's field options and\n * goes to the field unchanged, so an option TanStack adds works without a formdef release.\n */\nexport const OWN_ENTRY_KEYS = [\n 'widget',\n 'props',\n 'fields',\n 'layout',\n 'item',\n 'newItem',\n 'addLabel',\n 'removeLabel',\n 'itemKey',\n 'render',\n 'visibleWhen',\n 'dependsOn',\n] as const\n\n/** A key formdef reads from a field entry. */\nexport type OwnEntryKey = (typeof OWN_ENTRY_KEYS)[number]\n\nconst OWN_ENTRY_KEY_SET: ReadonlySet<string> = new Set(OWN_ENTRY_KEYS)\n\n/** Splits a field entry into the keys formdef reads and TanStack's field options. */\nexport function splitEntry(entry: object): {\n own: Partial<Record<OwnEntryKey, unknown>>\n fieldOptions: Record<string, unknown>\n} {\n const entries = Object.entries(entry)\n\n // `fromEntries` defines each key as data, so a key named `__proto__` cannot set a prototype.\n return {\n own: Object.fromEntries(entries.filter(([key]) => OWN_ENTRY_KEY_SET.has(key))),\n fieldOptions: Object.fromEntries(entries.filter(([key]) => !OWN_ENTRY_KEY_SET.has(key))),\n }\n}\n","// Bundlers replace `process.env.NODE_ENV` in exactly this spelling; without one, `process` may not\n// exist at all.\n// biome-ignore lint/style/useNamingConvention: the name bundlers replace.\ndeclare const process: { env: { NODE_ENV?: string } } | undefined\n\nconst warnedOwners = new WeakSet<object>()\n\nconst isProduction = (): boolean =>\n typeof process !== 'undefined' && process.env.NODE_ENV === 'production'\n\n/** Warns once per `owner`, and never in production. */\nexport function warnOnce(owner: object, message: string): void {\n if (isProduction() || warnedOwners.has(owner)) {\n return\n }\n\n warnedOwners.add(owner)\n console.warn(message)\n}\n","import { type ComponentType, type ReactNode, use } from 'react'\n\nimport { declaresEmptyValue } from '../widget/declares-empty-value'\n\nimport { ArrayEntry } from './array-entry'\nimport { byName } from './by-name'\nimport { childPath } from './child-path'\nimport { Derived } from './derived'\nimport { pathPattern } from './path-pattern'\nimport type { AppFieldProps, RenderForm } from './render-form'\nimport { NO_REQUIRED_PATHS, RequiredPaths } from './required-paths'\nimport { splitEntry } from './split-entry'\nimport { warnOnce } from './warn-once'\n\n// biome-ignore lint/suspicious/noExplicitAny: registered components take props of their own; the config types check them.\ntype AnyProps = any\n\ntype ComponentMap = Readonly<Record<string, ComponentType<AnyProps>>>\n\n/** The maps of a registry the renderer looks names up in. */\ntype RenderParts = {\n widgets: ComponentMap\n formComponents: ComponentMap\n structure: {\n section?: ComponentType<AnyProps>\n row?: ComponentType<AnyProps>\n arrayFrame?: ComponentType<AnyProps>\n }\n}\n\ntype LevelFields = Readonly<Record<string, object>>\n\ntype LayoutNodeEntry = string | Readonly<Record<string, unknown>>\n\n/** The props of {@link Fields}. */\nexport type FieldsProps = {\n parts: RenderParts\n form: RenderForm\n fields: LevelFields\n layout?: readonly unknown[] | undefined\n /** The path patterns of the required fields; none unless `createForm` renders the level. */\n requiredPaths?: ReadonlySet<string> | undefined\n}\n\ntype Level = Omit<FieldsProps, 'layout'> & {\n path: string | undefined\n /** Props an array frame passes the one widget of an item, through this level. */\n extra?: object | undefined\n}\n\ntype LevelProps = Level & {\n layout: readonly unknown[] | undefined\n}\n\ntype EntryProps = Omit<Level, 'fields'> & {\n entry: object\n path: string\n}\n\ntype FieldContentProps = Omit<EntryProps, 'entry'> & {\n entry: ReturnType<typeof splitEntry> & { source: object }\n props: object | undefined\n}\n\ntype ItemProps = Omit<EntryProps, 'extra'> & {\n index: number\n extra: object | undefined\n}\n\ntype LayoutNodeProps = {\n level: Level\n entry: LayoutNodeEntry\n}\n\ntype LayoutKindProps = {\n level: Level\n entry: Readonly<Record<string, unknown>>\n props: object | undefined\n}\n\nconst ARRAY_MODE = 'array'\n\nconst DROPPED_EXTRA = 'passes extra props to an item that has no single widget to receive them'\n\nconst renderNothing = (): ReactNode => null\n\nconst layoutOwner = (path: string | undefined): string =>\n path === undefined ? 'the form' : `\"${path}\"`\n\nconst isLeaf = (entry: unknown): boolean =>\n typeof entry === 'object' && entry !== null && 'widget' in entry\n\n// A frame's extra props have a single target only in a leaf, or an object level whose one field\n// is a leaf and which has no layout.\nconst takesExtra = (item: object): boolean => {\n if (isLeaf(item)) {\n return true\n }\n const { own } = splitEntry(item)\n const fields =\n typeof own.fields === 'object' && own.fields !== null ? Object.values(own.fields) : []\n\n return own.layout === undefined && fields.length === 1 && isLeaf(fields[0])\n}\n\nconst Item = ({ parts, form, entry, path, index, extra }: ItemProps): ReactNode => {\n const { own } = splitEntry(entry)\n const item = own.item as object\n const reachesWidget = extra !== undefined && takesExtra(item)\n if (extra !== undefined && !reachesWidget) {\n warnOnce(entry, `formdef: the array frame of \"${path}\" ${DROPPED_EXTRA}; they are dropped.`)\n }\n\n return (\n <Entry\n parts={parts}\n form={form}\n entry={item}\n path={childPath(path, index)}\n extra={reachesWidget ? extra : undefined}\n />\n )\n}\n\nconst FieldContent = ({ parts, form, entry, path, props, extra }: FieldContentProps): ReactNode => {\n const { own, fieldOptions } = entry\n const requiredPaths = use(RequiredPaths)\n\n if (typeof own.widget === 'string') {\n const Widget = byName(parts.widgets, own.widget)\n if (Widget === undefined) {\n throw new Error(`formdef: field \"${path}\" names the unregistered widget \"${own.widget}\".`)\n }\n\n // The config's `required` comes after the found one, so it wins.\n const found = declaresEmptyValue(Widget)\n ? { required: requiredPaths.has(pathPattern(path)) }\n : undefined\n\n return (\n <form.AppField {...fieldOptions} name={path}>\n {(field) => <Widget {...found} {...props} {...extra} field={field} />}\n </form.AppField>\n )\n }\n\n if (typeof own.render === 'function') {\n return (\n <form.AppField {...fieldOptions} name={path}>\n {own.render as AppFieldProps['children']}\n </form.AppField>\n )\n }\n\n if (typeof own.item === 'object' && own.item !== null) {\n return (\n <form.AppField {...fieldOptions} name={path} mode={ARRAY_MODE}>\n {(field) => (\n <ArrayEntry\n field={field}\n options={own}\n frame={parts.structure.arrayFrame}\n props={props}\n renderItem={(index, itemExtra) => (\n <Item\n parts={parts}\n form={form}\n entry={entry.source}\n path={path}\n index={index}\n extra={itemExtra}\n />\n )}\n />\n )}\n </form.AppField>\n )\n }\n\n if (typeof own.fields !== 'object' || own.fields === null) {\n return null\n }\n\n return (\n <>\n {Object.keys(fieldOptions).length > 0 && (\n <form.AppField {...fieldOptions} name={path}>\n {renderNothing}\n </form.AppField>\n )}\n <LevelEntries\n parts={parts}\n form={form}\n fields={own.fields as LevelFields}\n layout={Array.isArray(own.layout) ? own.layout : undefined}\n path={path}\n extra={extra}\n />\n </>\n )\n}\n\nconst Entry = ({ parts, form, entry, path, extra }: EntryProps): ReactNode => {\n const split = { ...splitEntry(entry), source: entry }\n\n return (\n <Derived form={form} entry={split.own}>\n {(props) => (\n <FieldContent\n parts={parts}\n form={form}\n entry={split}\n path={path}\n props={props}\n extra={extra}\n />\n )}\n </Derived>\n )\n}\n\nconst FieldNode = ({ level, name }: { level: Level; name: string }): ReactNode => {\n const entry = byName(level.fields, name)\n if (entry === undefined) {\n throw new Error(\n `formdef: the layout of ${layoutOwner(level.path)} names \"${name}\", which the level does not describe.`,\n )\n }\n\n return (\n <Entry\n parts={level.parts}\n form={level.form}\n entry={entry}\n path={childPath(level.path, name)}\n extra={level.extra}\n />\n )\n}\n\nconst LayoutNodes = ({ level, entries }: { level: Level; entries: unknown }): ReactNode =>\n (Array.isArray(entries) ? (entries as LayoutNodeEntry[]) : []).map((entry, index) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: a layout comes from the config and never reorders; a name may appear twice.\n <LayoutNode key={index} level={level} entry={entry} />\n ))\n\nconst SectionNode = ({ level, entry, props }: LayoutKindProps): ReactNode => {\n const children = <LayoutNodes level={level} entries={entry.layout} />\n const Section = level.parts.structure.section\n\n if (Section !== undefined) {\n return (\n <Section {...props} label={entry.section}>\n {children}\n </Section>\n )\n }\n\n return (\n <fieldset {...props}>\n <legend>{entry.section as string}</legend>\n {children}\n </fieldset>\n )\n}\n\nconst RowNode = ({ level, entry, props }: LayoutKindProps): ReactNode => {\n const children = <LayoutNodes level={level} entries={entry.row} />\n const Row = level.parts.structure.row\n\n if (Row !== undefined) {\n return <Row {...props}>{children}</Row>\n }\n\n return (\n <div {...props} data-formdef-row=\"\">\n {children}\n </div>\n )\n}\n\nconst ComponentNode = ({ level, entry, props }: LayoutKindProps): ReactNode => {\n const name = entry.component as string\n const Component = byName(level.parts.formComponents, name)\n if (Component === undefined) {\n throw new Error(\n `formdef: the layout of ${layoutOwner(level.path)} places the unregistered form component \"${name}\".`,\n )\n }\n\n return <Component {...props} />\n}\n\nconst LayoutKind = ({ level, entry, props }: LayoutKindProps): ReactNode => {\n if ('section' in entry) {\n return <SectionNode level={level} entry={entry} props={props} />\n }\n if ('row' in entry) {\n return <RowNode level={level} entry={entry} props={props} />\n }\n if (typeof entry.component === 'string') {\n return <ComponentNode level={level} entry={entry} props={props} />\n }\n if (typeof entry.render === 'function') {\n return (entry.render as (form: RenderForm) => ReactNode)(level.form)\n }\n\n return null\n}\n\nconst LayoutNode = ({ level, entry }: LayoutNodeProps): ReactNode => {\n if (typeof entry === 'string') {\n return <FieldNode level={level} name={entry} />\n }\n\n return (\n <Derived form={level.form} entry={entry}>\n {(props) => <LayoutKind level={level} entry={entry} props={props} />}\n </Derived>\n )\n}\n\nconst LevelEntries = ({ layout, ...level }: LevelProps): ReactNode => {\n if (layout !== undefined) {\n return <LayoutNodes level={level} entries={layout} />\n }\n\n return Object.keys(level.fields).map((name) => <FieldNode key={name} level={level} name={name} />)\n}\n\n/**\n * Renders the field entries of a level with the widgets of `parts`. Without a `layout` the entries\n * appear in the order `fields` declares them; with one, the layout decides the order, groups them\n * into sections and rows and places form components and inline entries. A leaf renders as its\n * widget, an object as its own level, a custom entry through its `render`, an array through the\n * registered `structure.arrayFrame` or the bare default. An object entry with TanStack options of\n * its own also mounts a field at its path, so its validators run. A `Fields` nested in a form finds\n * no required fields of its own, since its paths may be relative to a field group.\n */\nexport const Fields = ({ parts, form, fields, layout, requiredPaths }: FieldsProps): ReactNode => (\n <RequiredPaths value={requiredPaths ?? NO_REQUIRED_PATHS}>\n <LevelEntries parts={parts} form={form} fields={fields} layout={layout} path={undefined} />\n </RequiredPaths>\n)\n","import type { StandardSchemaV1 } from '@tanstack/react-form'\n\nimport { byName } from '../render/by-name'\nimport { childPath } from '../render/child-path'\nimport { pathPattern } from '../render/path-pattern'\nimport { splitEntry } from '../render/split-entry'\nimport { declaresEmptyValue } from '../widget/declares-empty-value'\n\ntype LevelFields = Readonly<Record<string, object>>\n\ntype Widgets = Readonly<Record<string, object>>\n\ntype RequiredPathsInput = {\n schema: unknown\n /** Whether the config validates the schema asynchronously (`validation.async`). */\n validatesAsync: boolean\n fields: LevelFields\n widgets: Widgets\n}\n\n/** The path patterns of the required fields, or why none could be found. */\ntype RequiredPathsResult = { paths: ReadonlySet<string> } | { problem: string }\n\ntype EmptyValues = {\n widgets: Widgets\n /** The path patterns of the leaves whose widget declares an empty value. */\n declared: Set<string>\n}\n\ntype Validation = ReturnType<StandardSchemaV1['~standard']['validate']>\ntype Issue = NonNullable<Exclude<Validation, Promise<unknown>>['issues']>[number]\n\nconst NONE: RequiredPathsResult = { paths: new Set() }\n\nconst ASYNC_SCHEMA =\n 'formdef: the schema validates asynchronously, so no field is found to be required; pass required in props.'\nconst UNSETTLED_SCHEMA =\n 'formdef: the schema did not validate empty values synchronously, so no field is found to be required; pass required in props.'\nconst THROWING_SCHEMA =\n 'formdef: the schema threw on empty values, so no field is found to be required'\n\nconst FIRST_ITEM = 0\n\nconst isStandardSchema = (schema: unknown): schema is StandardSchemaV1 =>\n typeof schema === 'object' && schema !== null && '~standard' in schema\n\nconst emptyLevel = (fields: LevelFields, path: string | undefined, empty: EmptyValues): object =>\n Object.fromEntries(\n Object.entries(fields).map(([key, entry]) => [\n key,\n emptyEntry(entry, childPath(path, key), empty),\n ]),\n )\n\nfunction emptyEntry(entry: object, path: string, empty: EmptyValues): unknown {\n const { own } = splitEntry(entry)\n\n if (typeof own.widget === 'string') {\n const widget = byName(empty.widgets, own.widget)\n if (widget === undefined || !declaresEmptyValue(widget)) {\n return undefined\n }\n empty.declared.add(pathPattern(path))\n\n return widget.emptyValue\n }\n if (typeof own.item === 'object' && own.item !== null) {\n return [emptyEntry(own.item, childPath(path, FIRST_ITEM), empty)]\n }\n if (typeof own.fields === 'object' && own.fields !== null) {\n return emptyLevel(own.fields as LevelFields, path, empty)\n }\n\n return undefined\n}\n\nconst issuePath = (issue: Issue): string =>\n pathPattern(\n (issue.path ?? []).reduce<string | undefined>((parent, segment) => {\n const key = typeof segment === 'object' ? segment.key : segment\n\n return childPath(parent, typeof key === 'number' ? key : String(key))\n }, undefined) ?? '',\n )\n\nconst parentOf = (path: string): string =>\n path.slice(0, Math.max(path.lastIndexOf('.'), path.lastIndexOf('[')))\n\n// A leaf's value may be an object or a list, so an issue below a leaf's path is the leaf's.\nconst leafOf = (path: string, leaves: ReadonlySet<string>): string | undefined => {\n for (let candidate = path; candidate !== ''; candidate = parentOf(candidate)) {\n if (leaves.has(candidate)) {\n return candidate\n }\n }\n\n return undefined\n}\n\n// The promise is dropped unread; a rejection is the schema failing on empty values, not the form.\nconst ignoreRejection = (): undefined => undefined\n\nconst errorText = (error: unknown): string =>\n error instanceof Error ? error.message : String(error)\n\n/**\n * Finds the required fields of a config: validates `schema` once, synchronously, with every leaf\n * holding its widget's empty value and every array one such item. A leaf whose widget declares an\n * empty value and whose path receives an issue is required. A schema the config validates\n * asynchronously is not run, so its asynchronous checks send nothing.\n */\nexport function findRequiredPaths({\n schema,\n validatesAsync,\n fields,\n widgets,\n}: RequiredPathsInput): RequiredPathsResult {\n if (!isStandardSchema(schema)) {\n return NONE\n }\n if (validatesAsync) {\n return { problem: ASYNC_SCHEMA }\n }\n\n const empty: EmptyValues = { widgets, declared: new Set() }\n const values = emptyLevel(fields, undefined, empty)\n let result: Validation\n try {\n result = schema['~standard'].validate(values)\n } catch (error) {\n return { problem: `${THROWING_SCHEMA}: ${errorText(error)}` }\n }\n if (result instanceof Promise) {\n result.then(undefined, ignoreRejection)\n\n return { problem: UNSETTLED_SCHEMA }\n }\n\n const leaves = (result.issues ?? []).map((issue) => leafOf(issuePath(issue), empty.declared))\n\n return { paths: new Set(leaves.filter((leaf) => leaf !== undefined)) }\n}\n","import {\n defaultValidationLogic,\n type ValidationLogicFn,\n type ValidationLogicProps,\n} from '@tanstack/react-form'\n\ntype ScheduledValidators = Parameters<ValidationLogicProps['runValidation']>[0]['validators']\n\n// Gated on the form, not the changed field: the schema validates the whole form, and TanStack names\n// the changed field only on the field-level call, where the schema does not sit.\nconst isSchemaDue = ({ event, form }: ValidationLogicProps): boolean => {\n if (event.type === 'blur' || event.type === 'submit') {\n return true\n }\n\n return event.type === 'change' && !form.state.isValid\n}\n\n/**\n * The validation logic of the `blurThenChange` mode: TanStack's default logic, plus the schema in\n * `onDynamic` when a field is left, on submit, and on every change while the form is invalid.\n */\nexport const blurThenChange: ValidationLogicFn = (props) => {\n let defaults: ScheduledValidators = []\n defaultValidationLogic({\n ...props,\n runValidation: ({ validators }) => {\n defaults = validators\n },\n })\n\n const schema = props.event.async ? props.validators?.onDynamicAsync : props.validators?.onDynamic\n if (schema === undefined || !isSchemaDue(props)) {\n return props.runValidation({ validators: defaults, form: props.form })\n }\n\n return props.runValidation({\n validators: [...defaults, { fn: schema, cause: 'dynamic' }],\n form: props.form,\n })\n}\n","import { revalidateLogic, type ValidationLogicFn } from '@tanstack/react-form'\n\nimport type { Validation } from '../config/validation'\nimport { blurThenChange } from './blur-then-change'\n\ntype Mode = NonNullable<Validation['mode']>\ntype DynamicMode = 'revalidate' | 'blurThenChange'\ntype Slot = 'onDynamic' | 'onChange' | 'onBlur' | 'onSubmit'\n\ntype Options = Readonly<Record<string, unknown>>\n\ntype FormOptionsInput = {\n /** The form config: the schema, formdef's own keys and TanStack's form options. */\n config: Options\n /** The instance's TanStack form options, from the form component's `options` prop. */\n options: Options | undefined\n}\n\nconst DEFAULT_MODE: Mode = 'revalidate'\n\nconst isDynamic = (mode: Mode): mode is DynamicMode =>\n mode === 'revalidate' || mode === 'blurThenChange'\n\nconst logicFor = (mode: DynamicMode): ValidationLogicFn =>\n mode === 'revalidate' ? revalidateLogic() : blurThenChange\n\nconst slotFor = (mode: Mode, isAsync: boolean): Slot | `${Slot}Async` => {\n const slot: Slot = isDynamic(mode) ? 'onDynamic' : mode\n\n return isAsync ? `${slot}Async` : slot\n}\n\nconst recordOf = (value: unknown): Options =>\n typeof value === 'object' && value !== null ? (value as Options) : {}\n\n// An instance passing `undefined`, such as data still loading, leaves the config's value in place.\nconst setEntriesOf = (value: unknown): Options =>\n Object.fromEntries(Object.entries(recordOf(value)).filter(([, entry]) => entry !== undefined))\n\n/**\n * The TanStack form options of a form: the config's options and the instance's merged key by key,\n * the instance winning, except `validators` and `listeners`, which merge slot by slot; a key or\n * slot the instance sets to `undefined` counts as unset. The schema\n * goes into the slot `validation` picks unless a validator already fills it; a dynamic mode brings\n * its validation logic, an asynchronous schema its debounce unless the options set one.\n */\nexport function formOptionsFor({ config, options }: FormOptionsInput): Record<string, unknown> {\n const {\n schema,\n validation,\n fields: _renderedByFields,\n layout: _arrangedByFields,\n ...configOptions\n } = config\n const { mode = DEFAULT_MODE, ...timing } = recordOf(validation) as Validation\n const isAsync = timing.async === true\n const slot = slotFor(mode, isAsync)\n const instanceOptions = setEntriesOf(options)\n const validators = {\n ...recordOf(configOptions.validators),\n ...setEntriesOf(instanceOptions.validators),\n }\n\n return {\n ...(isDynamic(mode) && { validationLogic: logicFor(mode) }),\n ...(isAsync &&\n timing.asyncDebounceMs !== undefined && { asyncDebounceMs: timing.asyncDebounceMs }),\n ...configOptions,\n ...instanceOptions,\n validators: validators[slot] === undefined ? { ...validators, [slot]: schema } : validators,\n listeners: { ...recordOf(configOptions.listeners), ...setEntriesOf(instanceOptions.listeners) },\n }\n}\n","import type { ComponentType, FormEvent, ReactNode } from 'react'\n\nimport type { FormElementProps } from '../config/form-props'\nimport type { BodyProps } from '../config/structure'\nimport { Fields, type FieldsProps } from '../render/fields'\nimport type { RenderForm } from '../render/render-form'\nimport { warnOnce } from '../render/warn-once'\nimport { findRequiredPaths } from './find-required-paths'\nimport { formOptionsFor } from './form-options-for'\n\ntype Options = Readonly<Record<string, unknown>>\n\n/** The form `useAppForm` returns, as far as the shell uses it. */\ntype ShellForm = RenderForm & {\n AppForm: ComponentType<{ children?: ReactNode }>\n handleSubmit: () => Promise<void>\n}\n\n/** What a registry binds a form to: its form hook and the maps the renderer looks names up in. */\nexport type FormBinding = {\n useAppForm: (options: Options) => ShellForm\n parts: FieldsProps['parts'] & {\n structure: { body?: ComponentType<BodyProps> }\n }\n}\n\n/** The props of a form component: the instance's options and what a `<form>` element takes. */\ntype FormComponentProps = FormElementProps & {\n options?: Options\n}\n\n/** A form config: TanStack's form options plus formdef's own keys. */\ntype ConfigInput = Options & {\n fields?: unknown\n layout?: unknown\n}\n\ntype ConfiguredFormProps = FormComponentProps & {\n binding: FormBinding\n config: ConfigInput\n requiredPaths: ReadonlySet<string>\n}\n\nconst MISSING_DEFAULT_VALUES =\n 'formdef: the form has no default values; set defaultValues in its config or in its options.'\n\nconst ConfiguredForm = ({\n binding,\n config,\n requiredPaths,\n options,\n ...formProps\n}: ConfiguredFormProps): ReactNode => {\n const formOptions = formOptionsFor({ config, options })\n if (formOptions.defaultValues === undefined) {\n throw new Error(MISSING_DEFAULT_VALUES)\n }\n\n const form = binding.useAppForm(formOptions)\n const Body = binding.parts.structure.body\n const content = (\n <Fields\n parts={binding.parts}\n form={form}\n fields={config.fields as FieldsProps['fields']}\n layout={config.layout as FieldsProps['layout']}\n requiredPaths={requiredPaths}\n />\n )\n const handleSubmit = (event: FormEvent<HTMLFormElement>): void => {\n event.preventDefault()\n event.stopPropagation()\n void form.handleSubmit()\n }\n\n return (\n <form.AppForm>\n <form noValidate {...formProps} onSubmit={handleSubmit}>\n {Body === undefined ? content : <Body>{content}</Body>}\n </form>\n </form.AppForm>\n )\n}\n\nconst isAsyncValidation = (validation: unknown): boolean =>\n typeof validation === 'object' && validation !== null && 'async' in validation\n ? validation.async === true\n : false\n\nconst requiredPathsOf = (binding: FormBinding, config: ConfigInput): ReadonlySet<string> => {\n const result = findRequiredPaths({\n schema: config.schema,\n validatesAsync: isAsyncValidation(config.validation),\n fields: (config.fields ?? {}) as Readonly<Record<string, object>>,\n widgets: binding.parts.widgets,\n })\n if ('problem' in result) {\n warnOnce(config, result.problem)\n\n return new Set()\n }\n\n return result.paths\n}\n\n/**\n * Turns a form config into a form component bound to `binding`: TanStack's form with the merged\n * options, a `<form>` element that submits through it, the registered body and the config's fields.\n * When it first renders, it finds the config's required fields once.\n */\nexport function createForm(\n binding: FormBinding,\n config: ConfigInput,\n): (props: FormComponentProps) => ReactNode {\n let requiredPaths: ReadonlySet<string> | undefined\n\n return (props: FormComponentProps): ReactNode => {\n requiredPaths ??= requiredPathsOf(binding, config)\n\n return (\n <ConfiguredForm {...props} binding={binding} config={config} requiredPaths={requiredPaths} />\n )\n }\n}\n","import { type AnyFieldApi, type AnyFormApi, createFormHook } from '@tanstack/react-form'\nimport { type Context, createElement, type ReactNode } from 'react'\n\nimport { createForm, type FormBinding } from '../form/create-form'\nimport { Fields, type FieldsProps } from '../render/fields'\n\nimport type {\n CheckedStructure,\n CheckedWidgets,\n Components,\n NewFormComponents,\n NewStructure,\n NewWidgets,\n Registry,\n Structure,\n} from './registry'\n\ntype Contexts = {\n fieldContext: Context<AnyFieldApi>\n formContext: Context<AnyFormApi>\n}\n\ntype RegistryParts<TWidgets, TFormComponents, TStructure> = Contexts & {\n widgets: TWidgets\n formComponents: TFormComponents\n structure: TStructure\n}\n\nfunction buildRegistry<\n TWidgets extends Components,\n TFormComponents extends Components,\n TStructure extends Structure,\n>(\n parts: RegistryParts<TWidgets, TFormComponents, TStructure>,\n): Registry<TWidgets, TFormComponents, TStructure> {\n const { extendForm: _replacedByExtendRegistry, ...formHook } = createFormHook({\n fieldContext: parts.fieldContext,\n formContext: parts.formContext,\n fieldComponents: parts.widgets,\n formComponents: parts.formComponents,\n })\n\n function extendRegistry<\n const TNewWidgets extends NewWidgets<TWidgets> = Record<never, never>,\n const TNewFormComponents extends NewFormComponents<TFormComponents> = Record<never, never>,\n const TNewStructure extends NewStructure<TStructure> = Record<never, never>,\n >(extension: {\n widgets?: TNewWidgets\n formComponents?: TNewFormComponents\n structure?: TNewStructure & CheckedStructure<TNewStructure>\n }): Registry<\n TWidgets & TNewWidgets,\n TFormComponents & TNewFormComponents,\n TStructure & TNewStructure\n > {\n const addedWidgets: Components = extension.widgets ?? {}\n const addedStructure: Structure = extension.structure ?? {}\n\n // The extension's maps are optional, and spreading an optional generic map drops its type.\n return buildRegistry({\n fieldContext: parts.fieldContext,\n formContext: parts.formContext,\n widgets: { ...parts.widgets, ...addedWidgets } as TWidgets & TNewWidgets,\n formComponents: { ...parts.formComponents, ...extension.formComponents } as TFormComponents &\n TNewFormComponents,\n structure: { ...parts.structure, ...addedStructure } as TStructure & TNewStructure,\n })\n }\n\n // TanStack's hook is generic over the form's types; the config's types are checked at the\n // registry's `createForm`, so the binding passes the merged options on untyped.\n const binding: FormBinding = {\n useAppForm: formHook.useAppForm as unknown as FormBinding['useAppForm'],\n parts: {\n widgets: parts.widgets,\n formComponents: parts.formComponents,\n structure: parts.structure,\n },\n }\n\n // biome-ignore lint/correctness/noComponentHookFactories: created once per registry, like TanStack's createFormHook.\n const BoundFields = (props: Omit<FieldsProps, 'parts'>): ReactNode =>\n createElement(Fields, { ...props, parts: binding.parts })\n\n return {\n formHook,\n createForm: (config) => createForm(binding, config),\n // The member's signature types the level by the form it is given; the renderer reads it untyped.\n Fields: BoundFields as unknown as Registry<TWidgets, TFormComponents, TStructure>['Fields'],\n // The member's signature checks the widgets' empty values; the implementation merges any map.\n extendRegistry: extendRegistry as Registry<\n TWidgets,\n TFormComponents,\n TStructure\n >['extendRegistry'],\n }\n}\n\n/**\n * Binds widgets, form components and structure slots to the app's TanStack contexts and returns a\n * registry, the value every form of the app is built with.\n *\n * Create the contexts with TanStack's `createFormHookContexts()` in a module of their own, so a\n * form component can read the form through `useFormContext()` without importing the registry.\n *\n * @typeParam TWidgets - The widgets by name; each takes its field as a prop typed `FieldOf<V>`.\n * @typeParam TFormComponents - The form components by name, placed in a layout by `{ component }`.\n * @typeParam TStructure - The structure slots: `body`, `section`, `row` and `arrayFrame`.\n * @param config - The contexts and what the registry holds.\n * @param config.fieldContext - The field context from `createFormHookContexts()`.\n * @param config.formContext - The form context from `createFormHookContexts()`.\n * @param config.widgets - The widgets by name. A widget's static `emptyValue`, the value it holds\n * while nothing is entered, must be of the value type it serves.\n * @param config.formComponents - The form components by name.\n * @param config.structure - The components that render the structure slots.\n * @defaultValue `config.formComponents` and `config.structure`: none registered.\n * @returns The registry.\n *\n * @example\n * ```tsx\n * 'use client'\n * import { createRegistry } from '@formdef/core'\n * import { fieldContext, formContext } from './context'\n *\n * export const registry = createRegistry({\n * fieldContext,\n * formContext,\n * widgets: { text: TextField, checkbox: CheckboxField },\n * formComponents: { SubmitButton },\n * structure: { section: Section },\n * })\n * ```\n */\nexport function createRegistry<\n const TWidgets extends Components,\n const TFormComponents extends Components = Record<never, never>,\n const TStructure extends Structure = Record<never, never>,\n>(\n config: Contexts & {\n widgets: TWidgets & CheckedWidgets<TWidgets>\n formComponents?: TFormComponents\n structure?: TStructure & CheckedStructure<TStructure>\n },\n): Registry<TWidgets, TFormComponents, TStructure> {\n // Both maps fall back to their empty defaults exactly when they are left out.\n return buildRegistry<TWidgets, TFormComponents, TStructure>({\n fieldContext: config.fieldContext,\n formContext: config.formContext,\n widgets: config.widgets,\n formComponents: config.formComponents ?? ({} as TFormComponents),\n structure: config.structure ?? ({} as TStructure),\n })\n}\n","import type { FieldApiOf } from './field-of'\n\nfunction textOf(error: unknown): string | undefined {\n if (typeof error === 'string') {\n return error\n }\n if (typeof error !== 'object' || error === null || !('message' in error)) {\n return undefined\n }\n\n return typeof error.message === 'string' ? error.message : undefined\n}\n\nfunction hasText(text: string | undefined): text is string {\n return text !== undefined && text !== ''\n}\n\n/**\n * Turns the errors of a field into the texts a widget renders.\n *\n * An error is either a string or an object with a `message`, such as an issue of a Standard\n * Schema. Errors without text are left out, and a text that several validators report appears\n * once, where it first occurs. A field's errors stay readable with TanStack's `disableErrorFlat`\n * option.\n *\n * @typeParam TValue - The value type of the field.\n * @param field - The field the widget renders.\n * @returns The distinct texts of the field's current errors, in order; empty when there are none.\n *\n * @example\n * ```tsx\n * const TextField = ({ field }: FieldOf<string>) => (\n * <>\n * <input value={field.state.value} onChange={(event) => field.handleChange(event.target.value)} />\n * <p>{errorsOf(field).join(' ')}</p>\n * </>\n * )\n * ```\n */\nexport function errorsOf<TValue>(field: FieldApiOf<TValue>): string[] {\n const texts = field.state.meta.errors.flat().map(textOf).filter(hasText)\n\n return [...new Set(texts)]\n}\n","const PATH_SEPARATORS = /[.[\\]]+/g\nconst SELECTOR_SAFE_SEPARATOR = '__'\n\n/**\n * Builds the DOM id that `useFieldParts` gives the control of a field, for code that has no\n * field at hand — an error summary above the form that links to each invalid field, for example.\n *\n * The id is the form's id, a hyphen, and the field's path with every run of dots and brackets\n * replaced by `__`. Fields of two different forms therefore never share an id, and the id can be\n * used in a CSS selector without escaping.\n *\n * @param formId - The id of the form, as TanStack Form reports it in `form.formId`.\n * @param path - The path of the field, such as `email`, `address.zip` or `contacts[0].name`.\n * @returns The id of the field's control, such as `signup-address__zip`.\n *\n * @remarks\n * The replacement cannot be reversed: a field literally named `a__b` gets the same id as the\n * path `a.b`.\n *\n * @example\n * ```tsx\n * <a href={`#${fieldId(form.formId, 'address.zip')}`}>Postal code</a>\n * ```\n */\nexport function fieldId(formId: string, path: string): string {\n return `${formId}-${path.replace(PATH_SEPARATORS, SELECTOR_SAFE_SEPARATOR)}`\n}\n","import { errorsOf } from './errors-of'\nimport { fieldId } from './field-id'\nimport type { FieldApiOf } from './field-of'\n\nconst DESCRIPTION_SUFFIX = '-description'\nconst ERROR_SUFFIX = '-error'\nconst IDREF_SEPARATOR = ' '\n\n/**\n * What {@link useFieldParts} returns: the wiring of a widget's control and what it needs to render\n * its description and its errors.\n */\nexport interface FieldParts {\n /** Props to spread onto the control; its value and change handler stay with the widget. */\n control: {\n /** The control's id, as {@link fieldId} builds it. */\n id: string\n /** The field's path, so the browser can autofill the control. */\n name: string\n /** Reports to the field that the control was left. */\n onBlur: () => void\n /** `true` while an error is due; absent otherwise, never `false`. */\n 'aria-invalid': true | undefined\n /** The ids of the description and of a due error with text, when there are any. */\n 'aria-describedby': string | undefined\n /** The id of the error element, while an error with text is due. */\n 'aria-errormessage': string | undefined\n }\n /** The id to give the element that renders the description. */\n descriptionId: string\n /** The id to give the element that renders the error texts. */\n errorId: string\n /**\n * Whether the widget shows the field's errors now: the field is invalid and was edited and\n * left, or the form was submitted.\n */\n isErrorDue: boolean\n /** The texts of the field's errors, as {@link errorsOf} returns them. */\n errors: string[]\n}\n\nfunction isErrorDueFor<TValue>(field: FieldApiOf<TValue>, wasSubmitted: boolean): boolean {\n const { isBlurred, isDirty, isValid } = field.state.meta\n const wasEditedAndLeft = isBlurred && isDirty\n\n return !isValid && (wasEditedAndLeft || wasSubmitted)\n}\n\nfunction idrefsOf(ids: (string | undefined)[]): string | undefined {\n const present = ids.filter((id) => id !== undefined)\n\n return present.length > 0 ? present.join(IDREF_SEPARATOR) : undefined\n}\n\n/**\n * The parts of `field` with the submit state passed in, so the result depends on nothing the\n * caller cannot see.\n */\nexport function fieldPartsFor<TValue>(\n field: FieldApiOf<TValue>,\n options: { description?: string | undefined; wasSubmitted: boolean },\n): FieldParts {\n const id = fieldId(field.form.formId, field.name)\n const descriptionId = `${id}${DESCRIPTION_SUFFIX}`\n const errorId = `${id}${ERROR_SUFFIX}`\n const isErrorDue = isErrorDueFor(field, options.wasSubmitted)\n const errors = errorsOf(field)\n const isErrorNamed = isErrorDue && errors.length > 0\n const hasDescription = options.description !== undefined && options.description !== ''\n\n return {\n control: {\n id,\n name: field.name,\n onBlur: field.handleBlur,\n 'aria-invalid': isErrorDue || undefined,\n 'aria-describedby': idrefsOf([\n hasDescription ? descriptionId : undefined,\n isErrorNamed ? errorId : undefined,\n ]),\n 'aria-errormessage': isErrorNamed ? errorId : undefined,\n },\n descriptionId,\n errorId,\n isErrorDue,\n errors,\n }\n}\n\n/**\n * Wires a widget's control to its field: its id and name, the blur report, and the aria\n * attributes that tie it to its description and its errors.\n *\n * An error is due once the field is invalid and was edited and left, or once the form was\n * submitted. While one is due, `aria-invalid` is `true`; while one with text is due, the control\n * points at the error element through `aria-describedby` and `aria-errormessage`. An id appears in\n * them only when its element is rendered.\n *\n * `fieldParts` reads whether the form was submitted without subscribing to it, so a widget whose\n * field state does not change on a submit keeps its error hidden until it renders again.\n *\n * @deprecated Use {@link useFieldParts}, which subscribes to the submit and renders the widget\n * again.\n * @typeParam TValue - The value type of the field.\n * @param field - The field the widget renders.\n * @param options - What the widget renders besides the control.\n * @param options.description - The description the widget renders below its label, if any. Only\n * whether it is a non-empty string is read.\n * @defaultValue `options.description`: `undefined`, no description is rendered.\n * @returns The props for the control, the ids for the description and error elements, whether an\n * error is due, and the error texts.\n *\n * @example\n * ```tsx\n * const TextField = ({ field, label, description }: FieldOf<string> & { label: string; description?: string }) => {\n * const { control, descriptionId, errorId, isErrorDue, errors } = fieldParts(field, { description })\n *\n * return (\n * <div>\n * <label htmlFor={control.id}>{label}</label>\n * {description && <p id={descriptionId}>{description}</p>}\n * <input {...control} value={field.state.value} onChange={(event) => field.handleChange(event.target.value)} />\n * {isErrorDue && errors.length > 0 && <p id={errorId}>{errors.join(' ')}</p>}\n * </div>\n * )\n * }\n * ```\n */\nexport function fieldParts<TValue>(\n field: FieldApiOf<TValue>,\n options: { description?: string | undefined } = {},\n): FieldParts {\n return fieldPartsFor(field, {\n ...options,\n wasSubmitted: field.form.state.submissionAttempts > 0,\n })\n}\n","import { useSelector } from '@tanstack/react-form'\n\nimport type { FieldApiOf } from './field-of'\nimport { type FieldParts, fieldPartsFor } from './field-parts'\n\n/**\n * Wires a widget's control to its field: its id and name, the blur report, and the aria\n * attributes that tie it to its description and its errors.\n *\n * An error is due once the field is invalid and was edited and left, or once the form was\n * submitted. While one is due, `aria-invalid` is `true`; while one with text is due, the control\n * points at the error element through `aria-describedby` and `aria-errormessage`. An id appears in\n * them only when its element is rendered.\n *\n * The hook subscribes to whether the form was submitted, so a submit renders the widget again even\n * when its field state does not change, as for a field that was left before without an edit.\n *\n * @typeParam TValue - The value type of the field.\n * @param field - The field the widget renders.\n * @param options - What the widget renders besides the control.\n * @param options.description - The description the widget renders below its label, if any. Only\n * whether it is a non-empty string is read.\n * @defaultValue `options.description`: `undefined`, no description is rendered.\n * @returns The props for the control, the ids for the description and error elements, whether an\n * error is due, and the error texts.\n *\n * @example\n * ```tsx\n * const TextField = ({ field, label, description }: FieldOf<string> & { label: string; description?: string }) => {\n * const { control, descriptionId, errorId, isErrorDue, errors } = useFieldParts(field, { description })\n *\n * return (\n * <div>\n * <label htmlFor={control.id}>{label}</label>\n * {description && <p id={descriptionId}>{description}</p>}\n * <input {...control} value={field.state.value} onChange={(event) => field.handleChange(event.target.value)} />\n * {isErrorDue && errors.length > 0 && <p id={errorId}>{errors.join(' ')}</p>}\n * </div>\n * )\n * }\n * ```\n */\nexport function useFieldParts<TValue>(\n field: FieldApiOf<TValue>,\n options: { description?: string | undefined } = {},\n): FieldParts {\n const wasSubmitted = useSelector(field.form.store, (state) => state.submissionAttempts > 0)\n\n return fieldPartsFor(field, { ...options, wasSubmitted })\n}\n"],"mappings":"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"}
package/dist/index.d.cts CHANGED
@@ -922,7 +922,7 @@ type LayoutOf<TRegistry extends AnyRegistry, TValue, TRoot = TValue> = LayoutEnt
922
922
  */
923
923
  export declare function errorsOf<TValue>(field: FieldApiOf<TValue>): string[];
924
924
  /**
925
- * Builds the DOM id that `fieldParts` gives the control of a field, for code that has no
925
+ * Builds the DOM id that `useFieldParts` gives the control of a field, for code that has no
926
926
  * field at hand — an error summary above the form that links to each invalid field, for example.
927
927
  *
928
928
  * The id is the form's id, a hyphen, and the field's path with every run of dots and brackets
@@ -944,7 +944,7 @@ export declare function errorsOf<TValue>(field: FieldApiOf<TValue>): string[];
944
944
  */
945
945
  export declare function fieldId(formId: string, path: string): string;
946
946
  /**
947
- * What {@link fieldParts} returns: the wiring of a widget's control and what it needs to render
947
+ * What {@link useFieldParts} returns: the wiring of a widget's control and what it needs to render
948
948
  * its description and its errors.
949
949
  */
950
950
  interface FieldParts {
@@ -984,6 +984,11 @@ interface FieldParts {
984
984
  * points at the error element through `aria-describedby` and `aria-errormessage`. An id appears in
985
985
  * them only when its element is rendered.
986
986
  *
987
+ * `fieldParts` reads whether the form was submitted without subscribing to it, so a widget whose
988
+ * field state does not change on a submit keeps its error hidden until it renders again.
989
+ *
990
+ * @deprecated Use {@link useFieldParts}, which subscribes to the submit and renders the widget
991
+ * again.
987
992
  * @typeParam TValue - The value type of the field.
988
993
  * @param field - The field the widget renders.
989
994
  * @param options - What the widget renders besides the control.
@@ -1012,5 +1017,45 @@ interface FieldParts {
1012
1017
  export declare function fieldParts<TValue>(field: FieldApiOf<TValue>, options?: {
1013
1018
  description?: string | undefined;
1014
1019
  }): FieldParts;
1020
+ /**
1021
+ * Wires a widget's control to its field: its id and name, the blur report, and the aria
1022
+ * attributes that tie it to its description and its errors.
1023
+ *
1024
+ * An error is due once the field is invalid and was edited and left, or once the form was
1025
+ * submitted. While one is due, `aria-invalid` is `true`; while one with text is due, the control
1026
+ * points at the error element through `aria-describedby` and `aria-errormessage`. An id appears in
1027
+ * them only when its element is rendered.
1028
+ *
1029
+ * The hook subscribes to whether the form was submitted, so a submit renders the widget again even
1030
+ * when its field state does not change, as for a field that was left before without an edit.
1031
+ *
1032
+ * @typeParam TValue - The value type of the field.
1033
+ * @param field - The field the widget renders.
1034
+ * @param options - What the widget renders besides the control.
1035
+ * @param options.description - The description the widget renders below its label, if any. Only
1036
+ * whether it is a non-empty string is read.
1037
+ * @defaultValue `options.description`: `undefined`, no description is rendered.
1038
+ * @returns The props for the control, the ids for the description and error elements, whether an
1039
+ * error is due, and the error texts.
1040
+ *
1041
+ * @example
1042
+ * ```tsx
1043
+ * const TextField = ({ field, label, description }: FieldOf<string> & { label: string; description?: string }) => {
1044
+ * const { control, descriptionId, errorId, isErrorDue, errors } = useFieldParts(field, { description })
1045
+ *
1046
+ * return (
1047
+ * <div>
1048
+ * <label htmlFor={control.id}>{label}</label>
1049
+ * {description && <p id={descriptionId}>{description}</p>}
1050
+ * <input {...control} value={field.state.value} onChange={(event) => field.handleChange(event.target.value)} />
1051
+ * {isErrorDue && errors.length > 0 && <p id={errorId}>{errors.join(' ')}</p>}
1052
+ * </div>
1053
+ * )
1054
+ * }
1055
+ * ```
1056
+ */
1057
+ export declare function useFieldParts<TValue>(field: FieldApiOf<TValue>, options?: {
1058
+ description?: string | undefined;
1059
+ }): FieldParts;
1015
1060
  export type { ArrayFrameProps, BodyProps, FieldApiOf, FieldOf, FieldParts, FieldsOf, FormConfig, FormHandle, FormProps, InferData, LayoutOf, Registry, RowProps, SectionProps, Validation };
1016
1061
  //# sourceMappingURL=index.d.cts.map
package/dist/index.d.ts CHANGED
@@ -922,7 +922,7 @@ type LayoutOf<TRegistry extends AnyRegistry, TValue, TRoot = TValue> = LayoutEnt
922
922
  */
923
923
  export declare function errorsOf<TValue>(field: FieldApiOf<TValue>): string[];
924
924
  /**
925
- * Builds the DOM id that `fieldParts` gives the control of a field, for code that has no
925
+ * Builds the DOM id that `useFieldParts` gives the control of a field, for code that has no
926
926
  * field at hand — an error summary above the form that links to each invalid field, for example.
927
927
  *
928
928
  * The id is the form's id, a hyphen, and the field's path with every run of dots and brackets
@@ -944,7 +944,7 @@ export declare function errorsOf<TValue>(field: FieldApiOf<TValue>): string[];
944
944
  */
945
945
  export declare function fieldId(formId: string, path: string): string;
946
946
  /**
947
- * What {@link fieldParts} returns: the wiring of a widget's control and what it needs to render
947
+ * What {@link useFieldParts} returns: the wiring of a widget's control and what it needs to render
948
948
  * its description and its errors.
949
949
  */
950
950
  interface FieldParts {
@@ -984,6 +984,11 @@ interface FieldParts {
984
984
  * points at the error element through `aria-describedby` and `aria-errormessage`. An id appears in
985
985
  * them only when its element is rendered.
986
986
  *
987
+ * `fieldParts` reads whether the form was submitted without subscribing to it, so a widget whose
988
+ * field state does not change on a submit keeps its error hidden until it renders again.
989
+ *
990
+ * @deprecated Use {@link useFieldParts}, which subscribes to the submit and renders the widget
991
+ * again.
987
992
  * @typeParam TValue - The value type of the field.
988
993
  * @param field - The field the widget renders.
989
994
  * @param options - What the widget renders besides the control.
@@ -1012,5 +1017,45 @@ interface FieldParts {
1012
1017
  export declare function fieldParts<TValue>(field: FieldApiOf<TValue>, options?: {
1013
1018
  description?: string | undefined;
1014
1019
  }): FieldParts;
1020
+ /**
1021
+ * Wires a widget's control to its field: its id and name, the blur report, and the aria
1022
+ * attributes that tie it to its description and its errors.
1023
+ *
1024
+ * An error is due once the field is invalid and was edited and left, or once the form was
1025
+ * submitted. While one is due, `aria-invalid` is `true`; while one with text is due, the control
1026
+ * points at the error element through `aria-describedby` and `aria-errormessage`. An id appears in
1027
+ * them only when its element is rendered.
1028
+ *
1029
+ * The hook subscribes to whether the form was submitted, so a submit renders the widget again even
1030
+ * when its field state does not change, as for a field that was left before without an edit.
1031
+ *
1032
+ * @typeParam TValue - The value type of the field.
1033
+ * @param field - The field the widget renders.
1034
+ * @param options - What the widget renders besides the control.
1035
+ * @param options.description - The description the widget renders below its label, if any. Only
1036
+ * whether it is a non-empty string is read.
1037
+ * @defaultValue `options.description`: `undefined`, no description is rendered.
1038
+ * @returns The props for the control, the ids for the description and error elements, whether an
1039
+ * error is due, and the error texts.
1040
+ *
1041
+ * @example
1042
+ * ```tsx
1043
+ * const TextField = ({ field, label, description }: FieldOf<string> & { label: string; description?: string }) => {
1044
+ * const { control, descriptionId, errorId, isErrorDue, errors } = useFieldParts(field, { description })
1045
+ *
1046
+ * return (
1047
+ * <div>
1048
+ * <label htmlFor={control.id}>{label}</label>
1049
+ * {description && <p id={descriptionId}>{description}</p>}
1050
+ * <input {...control} value={field.state.value} onChange={(event) => field.handleChange(event.target.value)} />
1051
+ * {isErrorDue && errors.length > 0 && <p id={errorId}>{errors.join(' ')}</p>}
1052
+ * </div>
1053
+ * )
1054
+ * }
1055
+ * ```
1056
+ */
1057
+ export declare function useFieldParts<TValue>(field: FieldApiOf<TValue>, options?: {
1058
+ description?: string | undefined;
1059
+ }): FieldParts;
1015
1060
  export type { ArrayFrameProps, BodyProps, FieldApiOf, FieldOf, FieldParts, FieldsOf, FormConfig, FormHandle, FormProps, InferData, LayoutOf, Registry, RowProps, SectionProps, Validation };
1016
1061
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -682,7 +682,7 @@ function errorsOf(field) {
682
682
  const PATH_SEPARATORS = /[.[\]]+/g;
683
683
  const SELECTOR_SAFE_SEPARATOR = "__";
684
684
  /**
685
- * Builds the DOM id that `fieldParts` gives the control of a field, for code that has no
685
+ * Builds the DOM id that `useFieldParts` gives the control of a field, for code that has no
686
686
  * field at hand — an error summary above the form that links to each invalid field, for example.
687
687
  *
688
688
  * The id is the form's id, a hyphen, and the field's path with every run of dots and brackets
@@ -709,17 +709,42 @@ function fieldId(formId, path) {
709
709
  const DESCRIPTION_SUFFIX = "-description";
710
710
  const ERROR_SUFFIX = "-error";
711
711
  const IDREF_SEPARATOR = " ";
712
- function isErrorDueFor(field) {
712
+ function isErrorDueFor(field, wasSubmitted) {
713
713
  const { isBlurred, isDirty, isValid } = field.state.meta;
714
- const wasEditedAndLeft = isBlurred && isDirty;
715
- const wasSubmitted = field.form.state.submissionAttempts > 0;
716
- return !isValid && (wasEditedAndLeft || wasSubmitted);
714
+ return !isValid && (isBlurred && isDirty || wasSubmitted);
717
715
  }
718
716
  function idrefsOf(ids) {
719
717
  const present = ids.filter((id) => id !== void 0);
720
718
  return present.length > 0 ? present.join(IDREF_SEPARATOR) : void 0;
721
719
  }
722
720
  /**
721
+ * The parts of `field` with the submit state passed in, so the result depends on nothing the
722
+ * caller cannot see.
723
+ */
724
+ function fieldPartsFor(field, options) {
725
+ const id = fieldId(field.form.formId, field.name);
726
+ const descriptionId = `${id}${DESCRIPTION_SUFFIX}`;
727
+ const errorId = `${id}${ERROR_SUFFIX}`;
728
+ const isErrorDue = isErrorDueFor(field, options.wasSubmitted);
729
+ const errors = errorsOf(field);
730
+ const isErrorNamed = isErrorDue && errors.length > 0;
731
+ const hasDescription = options.description !== void 0 && options.description !== "";
732
+ return {
733
+ control: {
734
+ id,
735
+ name: field.name,
736
+ onBlur: field.handleBlur,
737
+ "aria-invalid": isErrorDue || void 0,
738
+ "aria-describedby": idrefsOf([hasDescription ? descriptionId : void 0, isErrorNamed ? errorId : void 0]),
739
+ "aria-errormessage": isErrorNamed ? errorId : void 0
740
+ },
741
+ descriptionId,
742
+ errorId,
743
+ isErrorDue,
744
+ errors
745
+ };
746
+ }
747
+ /**
723
748
  * Wires a widget's control to its field: its id and name, the blur report, and the aria
724
749
  * attributes that tie it to its description and its errors.
725
750
  *
@@ -728,6 +753,11 @@ function idrefsOf(ids) {
728
753
  * points at the error element through `aria-describedby` and `aria-errormessage`. An id appears in
729
754
  * them only when its element is rendered.
730
755
  *
756
+ * `fieldParts` reads whether the form was submitted without subscribing to it, so a widget whose
757
+ * field state does not change on a submit keeps its error hidden until it renders again.
758
+ *
759
+ * @deprecated Use {@link useFieldParts}, which subscribes to the submit and renders the widget
760
+ * again.
731
761
  * @typeParam TValue - The value type of the field.
732
762
  * @param field - The field the widget renders.
733
763
  * @param options - What the widget renders besides the control.
@@ -754,28 +784,56 @@ function idrefsOf(ids) {
754
784
  * ```
755
785
  */
756
786
  function fieldParts(field, options = {}) {
757
- const id = fieldId(field.form.formId, field.name);
758
- const descriptionId = `${id}${DESCRIPTION_SUFFIX}`;
759
- const errorId = `${id}${ERROR_SUFFIX}`;
760
- const isErrorDue = isErrorDueFor(field);
761
- const errors = errorsOf(field);
762
- const isErrorNamed = isErrorDue && errors.length > 0;
763
- const hasDescription = options.description !== void 0 && options.description !== "";
764
- return {
765
- control: {
766
- id,
767
- name: field.name,
768
- onBlur: field.handleBlur,
769
- "aria-invalid": isErrorDue || void 0,
770
- "aria-describedby": idrefsOf([hasDescription ? descriptionId : void 0, isErrorNamed ? errorId : void 0]),
771
- "aria-errormessage": isErrorNamed ? errorId : void 0
772
- },
773
- descriptionId,
774
- errorId,
775
- isErrorDue,
776
- errors
777
- };
787
+ return fieldPartsFor(field, {
788
+ ...options,
789
+ wasSubmitted: field.form.state.submissionAttempts > 0
790
+ });
791
+ }
792
+ // src/widget/use-field-parts.ts
793
+ /**
794
+ * Wires a widget's control to its field: its id and name, the blur report, and the aria
795
+ * attributes that tie it to its description and its errors.
796
+ *
797
+ * An error is due once the field is invalid and was edited and left, or once the form was
798
+ * submitted. While one is due, `aria-invalid` is `true`; while one with text is due, the control
799
+ * points at the error element through `aria-describedby` and `aria-errormessage`. An id appears in
800
+ * them only when its element is rendered.
801
+ *
802
+ * The hook subscribes to whether the form was submitted, so a submit renders the widget again even
803
+ * when its field state does not change, as for a field that was left before without an edit.
804
+ *
805
+ * @typeParam TValue - The value type of the field.
806
+ * @param field - The field the widget renders.
807
+ * @param options - What the widget renders besides the control.
808
+ * @param options.description - The description the widget renders below its label, if any. Only
809
+ * whether it is a non-empty string is read.
810
+ * @defaultValue `options.description`: `undefined`, no description is rendered.
811
+ * @returns The props for the control, the ids for the description and error elements, whether an
812
+ * error is due, and the error texts.
813
+ *
814
+ * @example
815
+ * ```tsx
816
+ * const TextField = ({ field, label, description }: FieldOf<string> & { label: string; description?: string }) => {
817
+ * const { control, descriptionId, errorId, isErrorDue, errors } = useFieldParts(field, { description })
818
+ *
819
+ * return (
820
+ * <div>
821
+ * <label htmlFor={control.id}>{label}</label>
822
+ * {description && <p id={descriptionId}>{description}</p>}
823
+ * <input {...control} value={field.state.value} onChange={(event) => field.handleChange(event.target.value)} />
824
+ * {isErrorDue && errors.length > 0 && <p id={errorId}>{errors.join(' ')}</p>}
825
+ * </div>
826
+ * )
827
+ * }
828
+ * ```
829
+ */
830
+ function useFieldParts(field, options = {}) {
831
+ const wasSubmitted = useSelector(field.form.store, (state) => state.submissionAttempts > 0);
832
+ return fieldPartsFor(field, {
833
+ ...options,
834
+ wasSubmitted
835
+ });
778
836
  }
779
- export { createRegistry, errorsOf, fieldId, fieldParts };
837
+ export { createRegistry, errorsOf, fieldId, fieldParts, useFieldParts };
780
838
 
781
839
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/widget/declares-empty-value.ts","../src/render/array-entry.tsx","../src/render/by-name.ts","../src/render/child-path.ts","../src/render/derived.tsx","../src/render/path-pattern.ts","../src/render/required-paths.ts","../src/render/split-entry.ts","../src/render/warn-once.ts","../src/render/fields.tsx","../src/form/find-required-paths.ts","../src/form/blur-then-change.ts","../src/form/form-options-for.ts","../src/form/create-form.tsx","../src/registry/create-registry.ts","../src/widget/errors-of.ts","../src/widget/field-id.ts","../src/widget/field-parts.ts"],"sourcesContent":["/** Whether `widget` declares its empty value, the value it holds while nothing is entered. */\nexport function declaresEmptyValue(widget: object): widget is { emptyValue: unknown } {\n return Object.hasOwn(widget, 'emptyValue')\n}\n","import { type AnyFieldApi, useSelector } from '@tanstack/react-form'\nimport { type ComponentType, Fragment, type ReactNode } from 'react'\n\n// biome-ignore lint/suspicious/noExplicitAny: a registered frame takes props of its own; the config types check them.\ntype AnyProps = any\n\n/** The keys of an array entry the renderer reads besides its item. */\ntype ArrayOptions = {\n newItem?: unknown\n addLabel?: unknown\n removeLabel?: unknown\n itemKey?: unknown\n}\n\ntype ArrayEntryProps = {\n /** The array field, mounted at the entry's path. */\n field: AnyFieldApi\n options: ArrayOptions\n /** The registered `structure.arrayFrame`, if any. */\n frame: ComponentType<AnyProps> | undefined\n /** The entry's props for the frame. */\n props: object | undefined\n renderItem: (index: number, extra?: object) => ReactNode\n}\n\ntype ItemFunction<TResult> = (item: unknown, index?: number) => TResult\n\n/** What the controls of an array entry show: one key and one remove label per item. */\ntype ItemControls = {\n keys: (string | number)[]\n removeLabels: string[]\n}\n\nconst removeLabelOf = (removeLabel: unknown, item: unknown, index: number): string =>\n typeof removeLabel === 'function'\n ? (removeLabel as ItemFunction<string>)(item, index)\n : String(removeLabel)\n\nconst itemControlsOf = (options: ArrayOptions, value: unknown): ItemControls => {\n const items: unknown[] = Array.isArray(value) ? value : []\n\n return {\n keys: items.map((item, index) =>\n typeof options.itemKey === 'function'\n ? (options.itemKey as ItemFunction<string | number>)(item)\n : index,\n ),\n removeLabels: items.map((item, index) => removeLabelOf(options.removeLabel, item, index)),\n }\n}\n\nconst isSameList = <TItem,>(first: readonly TItem[], second: readonly TItem[]): boolean =>\n first.length === second.length && first.every((item, index) => item === second[index])\n\nconst isSameControls = (first: ItemControls, second: ItemControls): boolean =>\n isSameList(first.keys, second.keys) && isSameList(first.removeLabels, second.removeLabels)\n\n/**\n * Renders an array entry: through the registered frame, which receives `ArrayFrameProps`, or as\n * the bare default, each item followed by its remove button and one add button at the end.\n */\nexport const ArrayEntry = ({\n field,\n options,\n frame: Frame,\n props,\n renderItem,\n}: ArrayEntryProps): ReactNode => {\n // The field renders in TanStack's array mode, so only a change to the list reaches it; keys and\n // labels that name an item follow the items through a selection of their own.\n const { keys, removeLabels } = useSelector(\n field.store,\n (state: { value: unknown }) => itemControlsOf(options, state.value),\n { compare: isSameControls },\n )\n const newItem = options.newItem as () => unknown\n const addLabel = String(options.addLabel)\n const add = (): void => field.pushValue(newItem())\n const remove = (index: number): void => field.removeValue(index)\n\n if (Frame === undefined) {\n return (\n <>\n {keys.map((key, index) => (\n <Fragment key={key}>\n {renderItem(index)}\n <button type=\"button\" data-formdef-remove=\"\" onClick={() => remove(index)}>\n {removeLabels[index]}\n </button>\n </Fragment>\n ))}\n <button type=\"button\" data-formdef-add=\"\" onClick={add}>\n {addLabel}\n </button>\n </>\n )\n }\n\n return (\n <Frame\n {...props}\n field={field}\n keys={keys}\n renderItem={renderItem}\n addLabel={addLabel}\n removeLabels={removeLabels}\n add={add}\n remove={remove}\n insert={(index: number, item?: unknown) =>\n field.insertValue(index, item === undefined ? newItem() : item)\n }\n move={(from: number, to: number) => field.moveValue(from, to)}\n swap={(first: number, second: number) => field.swapValues(first, second)}\n replace={(index: number, item: unknown) => field.replaceValue(index, item)}\n clear={() => field.clearValues()}\n />\n )\n}\n","/** What `map` holds under `name` itself; a name every object inherits finds nothing. */\nexport function byName<TValue>(\n map: Readonly<Record<string, TValue>>,\n name: string,\n): TValue | undefined {\n return Object.hasOwn(map, name) ? map[name] : undefined\n}\n","/**\n * The path of `key` below `parent`: a key of the root level by itself, a key of a nested level\n * after a dot, the index of an array item in brackets.\n */\nexport function childPath(parent: string | undefined, key: string | number): string {\n if (typeof key === 'number') {\n return `${parent ?? ''}[${key}]`\n }\n\n return parent === undefined ? key : `${parent}.${key}`\n}\n","import { getBy } from '@tanstack/react-form'\nimport { type ReactNode, useRef } from 'react'\n\nimport type { RenderForm } from './render-form'\n\n/** What an entry reads from the form values: its condition, its props and the paths they read. */\ntype DerivedEntry = {\n visibleWhen?: unknown\n props?: unknown\n dependsOn?: unknown\n}\n\ntype DerivedProps = {\n form: RenderForm\n entry: DerivedEntry\n /** Builds the entry from its props; called only while the entry is visible. */\n children: (props: object | undefined) => ReactNode\n}\n\ntype LastRead = {\n entry: DerivedEntry\n dependencies: unknown[]\n selected: unknown\n}\n\nconst HIDDEN = Symbol('hidden')\nconst VISIBLE = Symbol('visible')\n\nconst propsOf = (props: unknown): object | undefined =>\n typeof props === 'object' && props !== null ? props : undefined\n\nconst isStale = (\n last: LastRead | undefined,\n entry: DerivedEntry,\n dependencies: unknown[],\n): boolean =>\n last === undefined ||\n last.entry !== entry ||\n dependencies.some((value, index) => !Object.is(value, last.dependencies[index]))\n\n/**\n * Renders an entry from what it reads from the form values. An entry with a condition or derived\n * props subscribes to the values; the selected result alone decides whether it re-renders: whether\n * it is shown for a condition, the values for derived props. `dependsOn` narrows when the selector\n * reads again, never what it returns. A hidden entry is never built.\n */\nexport const Derived = ({ form, entry, children }: DerivedProps): ReactNode => {\n const lastRead = useRef<LastRead | undefined>(undefined)\n const { visibleWhen, props, dependsOn } = entry\n const isConditional = typeof visibleWhen === 'function'\n const isDerived = typeof props === 'function'\n\n if (!(isConditional || isDerived)) {\n return children(propsOf(props))\n }\n\n const selectFrom = (values: unknown): unknown => {\n if (isConditional && !visibleWhen(values)) {\n return HIDDEN\n }\n\n return isDerived ? values : VISIBLE\n }\n\n const select = ({ values }: { values: unknown }): unknown => {\n if (!Array.isArray(dependsOn)) {\n return selectFrom(values)\n }\n\n const dependencies = dependsOn.map((path: string) => getBy(values, path))\n if (isStale(lastRead.current, entry, dependencies)) {\n lastRead.current = { entry, dependencies, selected: selectFrom(values) }\n }\n\n return lastRead.current?.selected\n }\n\n return (\n <form.Subscribe selector={select}>\n {(selected) => {\n if (selected === HIDDEN) {\n return null\n }\n\n return children(propsOf(isDerived ? props(selected) : props))\n }}\n </form.Subscribe>\n )\n}\n","const ITEM_INDEX = /\\[\\d+\\]/g\nconst ANY_ITEM = '[]'\n\n/** The path with every array index left open: `contacts[2].name` becomes `contacts[].name`. */\nexport function pathPattern(path: string): string {\n return path.replaceAll(ITEM_INDEX, ANY_ITEM)\n}\n","import { type Context, createContext } from 'react'\n\nexport const NO_REQUIRED_PATHS: ReadonlySet<string> = new Set()\n\n/** The path patterns of the required fields of the level being rendered. */\nexport const RequiredPaths: Context<ReadonlySet<string>> = createContext(NO_REQUIRED_PATHS)\n","/**\n * The keys formdef reads from a field entry. Every other key is one of TanStack's field options and\n * goes to the field unchanged, so an option TanStack adds works without a formdef release.\n */\nexport const OWN_ENTRY_KEYS = [\n 'widget',\n 'props',\n 'fields',\n 'layout',\n 'item',\n 'newItem',\n 'addLabel',\n 'removeLabel',\n 'itemKey',\n 'render',\n 'visibleWhen',\n 'dependsOn',\n] as const\n\n/** A key formdef reads from a field entry. */\nexport type OwnEntryKey = (typeof OWN_ENTRY_KEYS)[number]\n\nconst OWN_ENTRY_KEY_SET: ReadonlySet<string> = new Set(OWN_ENTRY_KEYS)\n\n/** Splits a field entry into the keys formdef reads and TanStack's field options. */\nexport function splitEntry(entry: object): {\n own: Partial<Record<OwnEntryKey, unknown>>\n fieldOptions: Record<string, unknown>\n} {\n const entries = Object.entries(entry)\n\n // `fromEntries` defines each key as data, so a key named `__proto__` cannot set a prototype.\n return {\n own: Object.fromEntries(entries.filter(([key]) => OWN_ENTRY_KEY_SET.has(key))),\n fieldOptions: Object.fromEntries(entries.filter(([key]) => !OWN_ENTRY_KEY_SET.has(key))),\n }\n}\n","// Bundlers replace `process.env.NODE_ENV` in exactly this spelling; without one, `process` may not\n// exist at all.\n// biome-ignore lint/style/useNamingConvention: the name bundlers replace.\ndeclare const process: { env: { NODE_ENV?: string } } | undefined\n\nconst warnedOwners = new WeakSet<object>()\n\nconst isProduction = (): boolean =>\n typeof process !== 'undefined' && process.env.NODE_ENV === 'production'\n\n/** Warns once per `owner`, and never in production. */\nexport function warnOnce(owner: object, message: string): void {\n if (isProduction() || warnedOwners.has(owner)) {\n return\n }\n\n warnedOwners.add(owner)\n console.warn(message)\n}\n","import { type ComponentType, type ReactNode, use } from 'react'\n\nimport { declaresEmptyValue } from '../widget/declares-empty-value'\n\nimport { ArrayEntry } from './array-entry'\nimport { byName } from './by-name'\nimport { childPath } from './child-path'\nimport { Derived } from './derived'\nimport { pathPattern } from './path-pattern'\nimport type { AppFieldProps, RenderForm } from './render-form'\nimport { NO_REQUIRED_PATHS, RequiredPaths } from './required-paths'\nimport { splitEntry } from './split-entry'\nimport { warnOnce } from './warn-once'\n\n// biome-ignore lint/suspicious/noExplicitAny: registered components take props of their own; the config types check them.\ntype AnyProps = any\n\ntype ComponentMap = Readonly<Record<string, ComponentType<AnyProps>>>\n\n/** The maps of a registry the renderer looks names up in. */\ntype RenderParts = {\n widgets: ComponentMap\n formComponents: ComponentMap\n structure: {\n section?: ComponentType<AnyProps>\n row?: ComponentType<AnyProps>\n arrayFrame?: ComponentType<AnyProps>\n }\n}\n\ntype LevelFields = Readonly<Record<string, object>>\n\ntype LayoutNodeEntry = string | Readonly<Record<string, unknown>>\n\n/** The props of {@link Fields}. */\nexport type FieldsProps = {\n parts: RenderParts\n form: RenderForm\n fields: LevelFields\n layout?: readonly unknown[] | undefined\n /** The path patterns of the required fields; none unless `createForm` renders the level. */\n requiredPaths?: ReadonlySet<string> | undefined\n}\n\ntype Level = Omit<FieldsProps, 'layout'> & {\n path: string | undefined\n /** Props an array frame passes the one widget of an item, through this level. */\n extra?: object | undefined\n}\n\ntype LevelProps = Level & {\n layout: readonly unknown[] | undefined\n}\n\ntype EntryProps = Omit<Level, 'fields'> & {\n entry: object\n path: string\n}\n\ntype FieldContentProps = Omit<EntryProps, 'entry'> & {\n entry: ReturnType<typeof splitEntry> & { source: object }\n props: object | undefined\n}\n\ntype ItemProps = Omit<EntryProps, 'extra'> & {\n index: number\n extra: object | undefined\n}\n\ntype LayoutNodeProps = {\n level: Level\n entry: LayoutNodeEntry\n}\n\ntype LayoutKindProps = {\n level: Level\n entry: Readonly<Record<string, unknown>>\n props: object | undefined\n}\n\nconst ARRAY_MODE = 'array'\n\nconst DROPPED_EXTRA = 'passes extra props to an item that has no single widget to receive them'\n\nconst renderNothing = (): ReactNode => null\n\nconst layoutOwner = (path: string | undefined): string =>\n path === undefined ? 'the form' : `\"${path}\"`\n\nconst isLeaf = (entry: unknown): boolean =>\n typeof entry === 'object' && entry !== null && 'widget' in entry\n\n// A frame's extra props have a single target only in a leaf, or an object level whose one field\n// is a leaf and which has no layout.\nconst takesExtra = (item: object): boolean => {\n if (isLeaf(item)) {\n return true\n }\n const { own } = splitEntry(item)\n const fields =\n typeof own.fields === 'object' && own.fields !== null ? Object.values(own.fields) : []\n\n return own.layout === undefined && fields.length === 1 && isLeaf(fields[0])\n}\n\nconst Item = ({ parts, form, entry, path, index, extra }: ItemProps): ReactNode => {\n const { own } = splitEntry(entry)\n const item = own.item as object\n const reachesWidget = extra !== undefined && takesExtra(item)\n if (extra !== undefined && !reachesWidget) {\n warnOnce(entry, `formdef: the array frame of \"${path}\" ${DROPPED_EXTRA}; they are dropped.`)\n }\n\n return (\n <Entry\n parts={parts}\n form={form}\n entry={item}\n path={childPath(path, index)}\n extra={reachesWidget ? extra : undefined}\n />\n )\n}\n\nconst FieldContent = ({ parts, form, entry, path, props, extra }: FieldContentProps): ReactNode => {\n const { own, fieldOptions } = entry\n const requiredPaths = use(RequiredPaths)\n\n if (typeof own.widget === 'string') {\n const Widget = byName(parts.widgets, own.widget)\n if (Widget === undefined) {\n throw new Error(`formdef: field \"${path}\" names the unregistered widget \"${own.widget}\".`)\n }\n\n // The config's `required` comes after the found one, so it wins.\n const found = declaresEmptyValue(Widget)\n ? { required: requiredPaths.has(pathPattern(path)) }\n : undefined\n\n return (\n <form.AppField {...fieldOptions} name={path}>\n {(field) => <Widget {...found} {...props} {...extra} field={field} />}\n </form.AppField>\n )\n }\n\n if (typeof own.render === 'function') {\n return (\n <form.AppField {...fieldOptions} name={path}>\n {own.render as AppFieldProps['children']}\n </form.AppField>\n )\n }\n\n if (typeof own.item === 'object' && own.item !== null) {\n return (\n <form.AppField {...fieldOptions} name={path} mode={ARRAY_MODE}>\n {(field) => (\n <ArrayEntry\n field={field}\n options={own}\n frame={parts.structure.arrayFrame}\n props={props}\n renderItem={(index, itemExtra) => (\n <Item\n parts={parts}\n form={form}\n entry={entry.source}\n path={path}\n index={index}\n extra={itemExtra}\n />\n )}\n />\n )}\n </form.AppField>\n )\n }\n\n if (typeof own.fields !== 'object' || own.fields === null) {\n return null\n }\n\n return (\n <>\n {Object.keys(fieldOptions).length > 0 && (\n <form.AppField {...fieldOptions} name={path}>\n {renderNothing}\n </form.AppField>\n )}\n <LevelEntries\n parts={parts}\n form={form}\n fields={own.fields as LevelFields}\n layout={Array.isArray(own.layout) ? own.layout : undefined}\n path={path}\n extra={extra}\n />\n </>\n )\n}\n\nconst Entry = ({ parts, form, entry, path, extra }: EntryProps): ReactNode => {\n const split = { ...splitEntry(entry), source: entry }\n\n return (\n <Derived form={form} entry={split.own}>\n {(props) => (\n <FieldContent\n parts={parts}\n form={form}\n entry={split}\n path={path}\n props={props}\n extra={extra}\n />\n )}\n </Derived>\n )\n}\n\nconst FieldNode = ({ level, name }: { level: Level; name: string }): ReactNode => {\n const entry = byName(level.fields, name)\n if (entry === undefined) {\n throw new Error(\n `formdef: the layout of ${layoutOwner(level.path)} names \"${name}\", which the level does not describe.`,\n )\n }\n\n return (\n <Entry\n parts={level.parts}\n form={level.form}\n entry={entry}\n path={childPath(level.path, name)}\n extra={level.extra}\n />\n )\n}\n\nconst LayoutNodes = ({ level, entries }: { level: Level; entries: unknown }): ReactNode =>\n (Array.isArray(entries) ? (entries as LayoutNodeEntry[]) : []).map((entry, index) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: a layout comes from the config and never reorders; a name may appear twice.\n <LayoutNode key={index} level={level} entry={entry} />\n ))\n\nconst SectionNode = ({ level, entry, props }: LayoutKindProps): ReactNode => {\n const children = <LayoutNodes level={level} entries={entry.layout} />\n const Section = level.parts.structure.section\n\n if (Section !== undefined) {\n return (\n <Section {...props} label={entry.section}>\n {children}\n </Section>\n )\n }\n\n return (\n <fieldset {...props}>\n <legend>{entry.section as string}</legend>\n {children}\n </fieldset>\n )\n}\n\nconst RowNode = ({ level, entry, props }: LayoutKindProps): ReactNode => {\n const children = <LayoutNodes level={level} entries={entry.row} />\n const Row = level.parts.structure.row\n\n if (Row !== undefined) {\n return <Row {...props}>{children}</Row>\n }\n\n return (\n <div {...props} data-formdef-row=\"\">\n {children}\n </div>\n )\n}\n\nconst ComponentNode = ({ level, entry, props }: LayoutKindProps): ReactNode => {\n const name = entry.component as string\n const Component = byName(level.parts.formComponents, name)\n if (Component === undefined) {\n throw new Error(\n `formdef: the layout of ${layoutOwner(level.path)} places the unregistered form component \"${name}\".`,\n )\n }\n\n return <Component {...props} />\n}\n\nconst LayoutKind = ({ level, entry, props }: LayoutKindProps): ReactNode => {\n if ('section' in entry) {\n return <SectionNode level={level} entry={entry} props={props} />\n }\n if ('row' in entry) {\n return <RowNode level={level} entry={entry} props={props} />\n }\n if (typeof entry.component === 'string') {\n return <ComponentNode level={level} entry={entry} props={props} />\n }\n if (typeof entry.render === 'function') {\n return (entry.render as (form: RenderForm) => ReactNode)(level.form)\n }\n\n return null\n}\n\nconst LayoutNode = ({ level, entry }: LayoutNodeProps): ReactNode => {\n if (typeof entry === 'string') {\n return <FieldNode level={level} name={entry} />\n }\n\n return (\n <Derived form={level.form} entry={entry}>\n {(props) => <LayoutKind level={level} entry={entry} props={props} />}\n </Derived>\n )\n}\n\nconst LevelEntries = ({ layout, ...level }: LevelProps): ReactNode => {\n if (layout !== undefined) {\n return <LayoutNodes level={level} entries={layout} />\n }\n\n return Object.keys(level.fields).map((name) => <FieldNode key={name} level={level} name={name} />)\n}\n\n/**\n * Renders the field entries of a level with the widgets of `parts`. Without a `layout` the entries\n * appear in the order `fields` declares them; with one, the layout decides the order, groups them\n * into sections and rows and places form components and inline entries. A leaf renders as its\n * widget, an object as its own level, a custom entry through its `render`, an array through the\n * registered `structure.arrayFrame` or the bare default. An object entry with TanStack options of\n * its own also mounts a field at its path, so its validators run. A `Fields` nested in a form finds\n * no required fields of its own, since its paths may be relative to a field group.\n */\nexport const Fields = ({ parts, form, fields, layout, requiredPaths }: FieldsProps): ReactNode => (\n <RequiredPaths value={requiredPaths ?? NO_REQUIRED_PATHS}>\n <LevelEntries parts={parts} form={form} fields={fields} layout={layout} path={undefined} />\n </RequiredPaths>\n)\n","import type { StandardSchemaV1 } from '@tanstack/react-form'\n\nimport { byName } from '../render/by-name'\nimport { childPath } from '../render/child-path'\nimport { pathPattern } from '../render/path-pattern'\nimport { splitEntry } from '../render/split-entry'\nimport { declaresEmptyValue } from '../widget/declares-empty-value'\n\ntype LevelFields = Readonly<Record<string, object>>\n\ntype Widgets = Readonly<Record<string, object>>\n\ntype RequiredPathsInput = {\n schema: unknown\n /** Whether the config validates the schema asynchronously (`validation.async`). */\n validatesAsync: boolean\n fields: LevelFields\n widgets: Widgets\n}\n\n/** The path patterns of the required fields, or why none could be found. */\ntype RequiredPathsResult = { paths: ReadonlySet<string> } | { problem: string }\n\ntype EmptyValues = {\n widgets: Widgets\n /** The path patterns of the leaves whose widget declares an empty value. */\n declared: Set<string>\n}\n\ntype Validation = ReturnType<StandardSchemaV1['~standard']['validate']>\ntype Issue = NonNullable<Exclude<Validation, Promise<unknown>>['issues']>[number]\n\nconst NONE: RequiredPathsResult = { paths: new Set() }\n\nconst ASYNC_SCHEMA =\n 'formdef: the schema validates asynchronously, so no field is found to be required; pass required in props.'\nconst UNSETTLED_SCHEMA =\n 'formdef: the schema did not validate empty values synchronously, so no field is found to be required; pass required in props.'\nconst THROWING_SCHEMA =\n 'formdef: the schema threw on empty values, so no field is found to be required'\n\nconst FIRST_ITEM = 0\n\nconst isStandardSchema = (schema: unknown): schema is StandardSchemaV1 =>\n typeof schema === 'object' && schema !== null && '~standard' in schema\n\nconst emptyLevel = (fields: LevelFields, path: string | undefined, empty: EmptyValues): object =>\n Object.fromEntries(\n Object.entries(fields).map(([key, entry]) => [\n key,\n emptyEntry(entry, childPath(path, key), empty),\n ]),\n )\n\nfunction emptyEntry(entry: object, path: string, empty: EmptyValues): unknown {\n const { own } = splitEntry(entry)\n\n if (typeof own.widget === 'string') {\n const widget = byName(empty.widgets, own.widget)\n if (widget === undefined || !declaresEmptyValue(widget)) {\n return undefined\n }\n empty.declared.add(pathPattern(path))\n\n return widget.emptyValue\n }\n if (typeof own.item === 'object' && own.item !== null) {\n return [emptyEntry(own.item, childPath(path, FIRST_ITEM), empty)]\n }\n if (typeof own.fields === 'object' && own.fields !== null) {\n return emptyLevel(own.fields as LevelFields, path, empty)\n }\n\n return undefined\n}\n\nconst issuePath = (issue: Issue): string =>\n pathPattern(\n (issue.path ?? []).reduce<string | undefined>((parent, segment) => {\n const key = typeof segment === 'object' ? segment.key : segment\n\n return childPath(parent, typeof key === 'number' ? key : String(key))\n }, undefined) ?? '',\n )\n\nconst parentOf = (path: string): string =>\n path.slice(0, Math.max(path.lastIndexOf('.'), path.lastIndexOf('[')))\n\n// A leaf's value may be an object or a list, so an issue below a leaf's path is the leaf's.\nconst leafOf = (path: string, leaves: ReadonlySet<string>): string | undefined => {\n for (let candidate = path; candidate !== ''; candidate = parentOf(candidate)) {\n if (leaves.has(candidate)) {\n return candidate\n }\n }\n\n return undefined\n}\n\n// The promise is dropped unread; a rejection is the schema failing on empty values, not the form.\nconst ignoreRejection = (): undefined => undefined\n\nconst errorText = (error: unknown): string =>\n error instanceof Error ? error.message : String(error)\n\n/**\n * Finds the required fields of a config: validates `schema` once, synchronously, with every leaf\n * holding its widget's empty value and every array one such item. A leaf whose widget declares an\n * empty value and whose path receives an issue is required. A schema the config validates\n * asynchronously is not run, so its asynchronous checks send nothing.\n */\nexport function findRequiredPaths({\n schema,\n validatesAsync,\n fields,\n widgets,\n}: RequiredPathsInput): RequiredPathsResult {\n if (!isStandardSchema(schema)) {\n return NONE\n }\n if (validatesAsync) {\n return { problem: ASYNC_SCHEMA }\n }\n\n const empty: EmptyValues = { widgets, declared: new Set() }\n const values = emptyLevel(fields, undefined, empty)\n let result: Validation\n try {\n result = schema['~standard'].validate(values)\n } catch (error) {\n return { problem: `${THROWING_SCHEMA}: ${errorText(error)}` }\n }\n if (result instanceof Promise) {\n result.then(undefined, ignoreRejection)\n\n return { problem: UNSETTLED_SCHEMA }\n }\n\n const leaves = (result.issues ?? []).map((issue) => leafOf(issuePath(issue), empty.declared))\n\n return { paths: new Set(leaves.filter((leaf) => leaf !== undefined)) }\n}\n","import {\n defaultValidationLogic,\n type ValidationLogicFn,\n type ValidationLogicProps,\n} from '@tanstack/react-form'\n\ntype ScheduledValidators = Parameters<ValidationLogicProps['runValidation']>[0]['validators']\n\n// Gated on the form, not the changed field: the schema validates the whole form, and TanStack names\n// the changed field only on the field-level call, where the schema does not sit.\nconst isSchemaDue = ({ event, form }: ValidationLogicProps): boolean => {\n if (event.type === 'blur' || event.type === 'submit') {\n return true\n }\n\n return event.type === 'change' && !form.state.isValid\n}\n\n/**\n * The validation logic of the `blurThenChange` mode: TanStack's default logic, plus the schema in\n * `onDynamic` when a field is left, on submit, and on every change while the form is invalid.\n */\nexport const blurThenChange: ValidationLogicFn = (props) => {\n let defaults: ScheduledValidators = []\n defaultValidationLogic({\n ...props,\n runValidation: ({ validators }) => {\n defaults = validators\n },\n })\n\n const schema = props.event.async ? props.validators?.onDynamicAsync : props.validators?.onDynamic\n if (schema === undefined || !isSchemaDue(props)) {\n return props.runValidation({ validators: defaults, form: props.form })\n }\n\n return props.runValidation({\n validators: [...defaults, { fn: schema, cause: 'dynamic' }],\n form: props.form,\n })\n}\n","import { revalidateLogic, type ValidationLogicFn } from '@tanstack/react-form'\n\nimport type { Validation } from '../config/validation'\nimport { blurThenChange } from './blur-then-change'\n\ntype Mode = NonNullable<Validation['mode']>\ntype DynamicMode = 'revalidate' | 'blurThenChange'\ntype Slot = 'onDynamic' | 'onChange' | 'onBlur' | 'onSubmit'\n\ntype Options = Readonly<Record<string, unknown>>\n\ntype FormOptionsInput = {\n /** The form config: the schema, formdef's own keys and TanStack's form options. */\n config: Options\n /** The instance's TanStack form options, from the form component's `options` prop. */\n options: Options | undefined\n}\n\nconst DEFAULT_MODE: Mode = 'revalidate'\n\nconst isDynamic = (mode: Mode): mode is DynamicMode =>\n mode === 'revalidate' || mode === 'blurThenChange'\n\nconst logicFor = (mode: DynamicMode): ValidationLogicFn =>\n mode === 'revalidate' ? revalidateLogic() : blurThenChange\n\nconst slotFor = (mode: Mode, isAsync: boolean): Slot | `${Slot}Async` => {\n const slot: Slot = isDynamic(mode) ? 'onDynamic' : mode\n\n return isAsync ? `${slot}Async` : slot\n}\n\nconst recordOf = (value: unknown): Options =>\n typeof value === 'object' && value !== null ? (value as Options) : {}\n\n// An instance passing `undefined`, such as data still loading, leaves the config's value in place.\nconst setEntriesOf = (value: unknown): Options =>\n Object.fromEntries(Object.entries(recordOf(value)).filter(([, entry]) => entry !== undefined))\n\n/**\n * The TanStack form options of a form: the config's options and the instance's merged key by key,\n * the instance winning, except `validators` and `listeners`, which merge slot by slot; a key or\n * slot the instance sets to `undefined` counts as unset. The schema\n * goes into the slot `validation` picks unless a validator already fills it; a dynamic mode brings\n * its validation logic, an asynchronous schema its debounce unless the options set one.\n */\nexport function formOptionsFor({ config, options }: FormOptionsInput): Record<string, unknown> {\n const {\n schema,\n validation,\n fields: _renderedByFields,\n layout: _arrangedByFields,\n ...configOptions\n } = config\n const { mode = DEFAULT_MODE, ...timing } = recordOf(validation) as Validation\n const isAsync = timing.async === true\n const slot = slotFor(mode, isAsync)\n const instanceOptions = setEntriesOf(options)\n const validators = {\n ...recordOf(configOptions.validators),\n ...setEntriesOf(instanceOptions.validators),\n }\n\n return {\n ...(isDynamic(mode) && { validationLogic: logicFor(mode) }),\n ...(isAsync &&\n timing.asyncDebounceMs !== undefined && { asyncDebounceMs: timing.asyncDebounceMs }),\n ...configOptions,\n ...instanceOptions,\n validators: validators[slot] === undefined ? { ...validators, [slot]: schema } : validators,\n listeners: { ...recordOf(configOptions.listeners), ...setEntriesOf(instanceOptions.listeners) },\n }\n}\n","import type { ComponentType, FormEvent, ReactNode } from 'react'\n\nimport type { FormElementProps } from '../config/form-props'\nimport type { BodyProps } from '../config/structure'\nimport { Fields, type FieldsProps } from '../render/fields'\nimport type { RenderForm } from '../render/render-form'\nimport { warnOnce } from '../render/warn-once'\nimport { findRequiredPaths } from './find-required-paths'\nimport { formOptionsFor } from './form-options-for'\n\ntype Options = Readonly<Record<string, unknown>>\n\n/** The form `useAppForm` returns, as far as the shell uses it. */\ntype ShellForm = RenderForm & {\n AppForm: ComponentType<{ children?: ReactNode }>\n handleSubmit: () => Promise<void>\n}\n\n/** What a registry binds a form to: its form hook and the maps the renderer looks names up in. */\nexport type FormBinding = {\n useAppForm: (options: Options) => ShellForm\n parts: FieldsProps['parts'] & {\n structure: { body?: ComponentType<BodyProps> }\n }\n}\n\n/** The props of a form component: the instance's options and what a `<form>` element takes. */\ntype FormComponentProps = FormElementProps & {\n options?: Options\n}\n\n/** A form config: TanStack's form options plus formdef's own keys. */\ntype ConfigInput = Options & {\n fields?: unknown\n layout?: unknown\n}\n\ntype ConfiguredFormProps = FormComponentProps & {\n binding: FormBinding\n config: ConfigInput\n requiredPaths: ReadonlySet<string>\n}\n\nconst MISSING_DEFAULT_VALUES =\n 'formdef: the form has no default values; set defaultValues in its config or in its options.'\n\nconst ConfiguredForm = ({\n binding,\n config,\n requiredPaths,\n options,\n ...formProps\n}: ConfiguredFormProps): ReactNode => {\n const formOptions = formOptionsFor({ config, options })\n if (formOptions.defaultValues === undefined) {\n throw new Error(MISSING_DEFAULT_VALUES)\n }\n\n const form = binding.useAppForm(formOptions)\n const Body = binding.parts.structure.body\n const content = (\n <Fields\n parts={binding.parts}\n form={form}\n fields={config.fields as FieldsProps['fields']}\n layout={config.layout as FieldsProps['layout']}\n requiredPaths={requiredPaths}\n />\n )\n const handleSubmit = (event: FormEvent<HTMLFormElement>): void => {\n event.preventDefault()\n event.stopPropagation()\n void form.handleSubmit()\n }\n\n return (\n <form.AppForm>\n <form noValidate {...formProps} onSubmit={handleSubmit}>\n {Body === undefined ? content : <Body>{content}</Body>}\n </form>\n </form.AppForm>\n )\n}\n\nconst isAsyncValidation = (validation: unknown): boolean =>\n typeof validation === 'object' && validation !== null && 'async' in validation\n ? validation.async === true\n : false\n\nconst requiredPathsOf = (binding: FormBinding, config: ConfigInput): ReadonlySet<string> => {\n const result = findRequiredPaths({\n schema: config.schema,\n validatesAsync: isAsyncValidation(config.validation),\n fields: (config.fields ?? {}) as Readonly<Record<string, object>>,\n widgets: binding.parts.widgets,\n })\n if ('problem' in result) {\n warnOnce(config, result.problem)\n\n return new Set()\n }\n\n return result.paths\n}\n\n/**\n * Turns a form config into a form component bound to `binding`: TanStack's form with the merged\n * options, a `<form>` element that submits through it, the registered body and the config's fields.\n * When it first renders, it finds the config's required fields once.\n */\nexport function createForm(\n binding: FormBinding,\n config: ConfigInput,\n): (props: FormComponentProps) => ReactNode {\n let requiredPaths: ReadonlySet<string> | undefined\n\n return (props: FormComponentProps): ReactNode => {\n requiredPaths ??= requiredPathsOf(binding, config)\n\n return (\n <ConfiguredForm {...props} binding={binding} config={config} requiredPaths={requiredPaths} />\n )\n }\n}\n","import { type AnyFieldApi, type AnyFormApi, createFormHook } from '@tanstack/react-form'\nimport { type Context, createElement, type ReactNode } from 'react'\n\nimport { createForm, type FormBinding } from '../form/create-form'\nimport { Fields, type FieldsProps } from '../render/fields'\n\nimport type {\n CheckedStructure,\n CheckedWidgets,\n Components,\n NewFormComponents,\n NewStructure,\n NewWidgets,\n Registry,\n Structure,\n} from './registry'\n\ntype Contexts = {\n fieldContext: Context<AnyFieldApi>\n formContext: Context<AnyFormApi>\n}\n\ntype RegistryParts<TWidgets, TFormComponents, TStructure> = Contexts & {\n widgets: TWidgets\n formComponents: TFormComponents\n structure: TStructure\n}\n\nfunction buildRegistry<\n TWidgets extends Components,\n TFormComponents extends Components,\n TStructure extends Structure,\n>(\n parts: RegistryParts<TWidgets, TFormComponents, TStructure>,\n): Registry<TWidgets, TFormComponents, TStructure> {\n const { extendForm: _replacedByExtendRegistry, ...formHook } = createFormHook({\n fieldContext: parts.fieldContext,\n formContext: parts.formContext,\n fieldComponents: parts.widgets,\n formComponents: parts.formComponents,\n })\n\n function extendRegistry<\n const TNewWidgets extends NewWidgets<TWidgets> = Record<never, never>,\n const TNewFormComponents extends NewFormComponents<TFormComponents> = Record<never, never>,\n const TNewStructure extends NewStructure<TStructure> = Record<never, never>,\n >(extension: {\n widgets?: TNewWidgets\n formComponents?: TNewFormComponents\n structure?: TNewStructure & CheckedStructure<TNewStructure>\n }): Registry<\n TWidgets & TNewWidgets,\n TFormComponents & TNewFormComponents,\n TStructure & TNewStructure\n > {\n const addedWidgets: Components = extension.widgets ?? {}\n const addedStructure: Structure = extension.structure ?? {}\n\n // The extension's maps are optional, and spreading an optional generic map drops its type.\n return buildRegistry({\n fieldContext: parts.fieldContext,\n formContext: parts.formContext,\n widgets: { ...parts.widgets, ...addedWidgets } as TWidgets & TNewWidgets,\n formComponents: { ...parts.formComponents, ...extension.formComponents } as TFormComponents &\n TNewFormComponents,\n structure: { ...parts.structure, ...addedStructure } as TStructure & TNewStructure,\n })\n }\n\n // TanStack's hook is generic over the form's types; the config's types are checked at the\n // registry's `createForm`, so the binding passes the merged options on untyped.\n const binding: FormBinding = {\n useAppForm: formHook.useAppForm as unknown as FormBinding['useAppForm'],\n parts: {\n widgets: parts.widgets,\n formComponents: parts.formComponents,\n structure: parts.structure,\n },\n }\n\n // biome-ignore lint/correctness/noComponentHookFactories: created once per registry, like TanStack's createFormHook.\n const BoundFields = (props: Omit<FieldsProps, 'parts'>): ReactNode =>\n createElement(Fields, { ...props, parts: binding.parts })\n\n return {\n formHook,\n createForm: (config) => createForm(binding, config),\n // The member's signature types the level by the form it is given; the renderer reads it untyped.\n Fields: BoundFields as unknown as Registry<TWidgets, TFormComponents, TStructure>['Fields'],\n // The member's signature checks the widgets' empty values; the implementation merges any map.\n extendRegistry: extendRegistry as Registry<\n TWidgets,\n TFormComponents,\n TStructure\n >['extendRegistry'],\n }\n}\n\n/**\n * Binds widgets, form components and structure slots to the app's TanStack contexts and returns a\n * registry, the value every form of the app is built with.\n *\n * Create the contexts with TanStack's `createFormHookContexts()` in a module of their own, so a\n * form component can read the form through `useFormContext()` without importing the registry.\n *\n * @typeParam TWidgets - The widgets by name; each takes its field as a prop typed `FieldOf<V>`.\n * @typeParam TFormComponents - The form components by name, placed in a layout by `{ component }`.\n * @typeParam TStructure - The structure slots: `body`, `section`, `row` and `arrayFrame`.\n * @param config - The contexts and what the registry holds.\n * @param config.fieldContext - The field context from `createFormHookContexts()`.\n * @param config.formContext - The form context from `createFormHookContexts()`.\n * @param config.widgets - The widgets by name. A widget's static `emptyValue`, the value it holds\n * while nothing is entered, must be of the value type it serves.\n * @param config.formComponents - The form components by name.\n * @param config.structure - The components that render the structure slots.\n * @defaultValue `config.formComponents` and `config.structure`: none registered.\n * @returns The registry.\n *\n * @example\n * ```tsx\n * 'use client'\n * import { createRegistry } from '@formdef/core'\n * import { fieldContext, formContext } from './context'\n *\n * export const registry = createRegistry({\n * fieldContext,\n * formContext,\n * widgets: { text: TextField, checkbox: CheckboxField },\n * formComponents: { SubmitButton },\n * structure: { section: Section },\n * })\n * ```\n */\nexport function createRegistry<\n const TWidgets extends Components,\n const TFormComponents extends Components = Record<never, never>,\n const TStructure extends Structure = Record<never, never>,\n>(\n config: Contexts & {\n widgets: TWidgets & CheckedWidgets<TWidgets>\n formComponents?: TFormComponents\n structure?: TStructure & CheckedStructure<TStructure>\n },\n): Registry<TWidgets, TFormComponents, TStructure> {\n // Both maps fall back to their empty defaults exactly when they are left out.\n return buildRegistry<TWidgets, TFormComponents, TStructure>({\n fieldContext: config.fieldContext,\n formContext: config.formContext,\n widgets: config.widgets,\n formComponents: config.formComponents ?? ({} as TFormComponents),\n structure: config.structure ?? ({} as TStructure),\n })\n}\n","import type { FieldApiOf } from './field-of'\n\nfunction textOf(error: unknown): string | undefined {\n if (typeof error === 'string') {\n return error\n }\n if (typeof error !== 'object' || error === null || !('message' in error)) {\n return undefined\n }\n\n return typeof error.message === 'string' ? error.message : undefined\n}\n\nfunction hasText(text: string | undefined): text is string {\n return text !== undefined && text !== ''\n}\n\n/**\n * Turns the errors of a field into the texts a widget renders.\n *\n * An error is either a string or an object with a `message`, such as an issue of a Standard\n * Schema. Errors without text are left out, and a text that several validators report appears\n * once, where it first occurs. A field's errors stay readable with TanStack's `disableErrorFlat`\n * option.\n *\n * @typeParam TValue - The value type of the field.\n * @param field - The field the widget renders.\n * @returns The distinct texts of the field's current errors, in order; empty when there are none.\n *\n * @example\n * ```tsx\n * const TextField = ({ field }: FieldOf<string>) => (\n * <>\n * <input value={field.state.value} onChange={(event) => field.handleChange(event.target.value)} />\n * <p>{errorsOf(field).join(' ')}</p>\n * </>\n * )\n * ```\n */\nexport function errorsOf<TValue>(field: FieldApiOf<TValue>): string[] {\n const texts = field.state.meta.errors.flat().map(textOf).filter(hasText)\n\n return [...new Set(texts)]\n}\n","const PATH_SEPARATORS = /[.[\\]]+/g\nconst SELECTOR_SAFE_SEPARATOR = '__'\n\n/**\n * Builds the DOM id that `fieldParts` gives the control of a field, for code that has no\n * field at hand — an error summary above the form that links to each invalid field, for example.\n *\n * The id is the form's id, a hyphen, and the field's path with every run of dots and brackets\n * replaced by `__`. Fields of two different forms therefore never share an id, and the id can be\n * used in a CSS selector without escaping.\n *\n * @param formId - The id of the form, as TanStack Form reports it in `form.formId`.\n * @param path - The path of the field, such as `email`, `address.zip` or `contacts[0].name`.\n * @returns The id of the field's control, such as `signup-address__zip`.\n *\n * @remarks\n * The replacement cannot be reversed: a field literally named `a__b` gets the same id as the\n * path `a.b`.\n *\n * @example\n * ```tsx\n * <a href={`#${fieldId(form.formId, 'address.zip')}`}>Postal code</a>\n * ```\n */\nexport function fieldId(formId: string, path: string): string {\n return `${formId}-${path.replace(PATH_SEPARATORS, SELECTOR_SAFE_SEPARATOR)}`\n}\n","import { errorsOf } from './errors-of'\nimport { fieldId } from './field-id'\nimport type { FieldApiOf } from './field-of'\n\nconst DESCRIPTION_SUFFIX = '-description'\nconst ERROR_SUFFIX = '-error'\nconst IDREF_SEPARATOR = ' '\n\n/**\n * What {@link fieldParts} returns: the wiring of a widget's control and what it needs to render\n * its description and its errors.\n */\nexport interface FieldParts {\n /** Props to spread onto the control; its value and change handler stay with the widget. */\n control: {\n /** The control's id, as {@link fieldId} builds it. */\n id: string\n /** The field's path, so the browser can autofill the control. */\n name: string\n /** Reports to the field that the control was left. */\n onBlur: () => void\n /** `true` while an error is due; absent otherwise, never `false`. */\n 'aria-invalid': true | undefined\n /** The ids of the description and of a due error with text, when there are any. */\n 'aria-describedby': string | undefined\n /** The id of the error element, while an error with text is due. */\n 'aria-errormessage': string | undefined\n }\n /** The id to give the element that renders the description. */\n descriptionId: string\n /** The id to give the element that renders the error texts. */\n errorId: string\n /**\n * Whether the widget shows the field's errors now: the field is invalid and was edited and\n * left, or the form was submitted.\n */\n isErrorDue: boolean\n /** The texts of the field's errors, as {@link errorsOf} returns them. */\n errors: string[]\n}\n\nfunction isErrorDueFor<TValue>(field: FieldApiOf<TValue>): boolean {\n const { isBlurred, isDirty, isValid } = field.state.meta\n const wasEditedAndLeft = isBlurred && isDirty\n const wasSubmitted = field.form.state.submissionAttempts > 0\n\n return !isValid && (wasEditedAndLeft || wasSubmitted)\n}\n\nfunction idrefsOf(ids: (string | undefined)[]): string | undefined {\n const present = ids.filter((id) => id !== undefined)\n\n return present.length > 0 ? present.join(IDREF_SEPARATOR) : undefined\n}\n\n/**\n * Wires a widget's control to its field: its id and name, the blur report, and the aria\n * attributes that tie it to its description and its errors.\n *\n * An error is due once the field is invalid and was edited and left, or once the form was\n * submitted. While one is due, `aria-invalid` is `true`; while one with text is due, the control\n * points at the error element through `aria-describedby` and `aria-errormessage`. An id appears in\n * them only when its element is rendered.\n *\n * @typeParam TValue - The value type of the field.\n * @param field - The field the widget renders.\n * @param options - What the widget renders besides the control.\n * @param options.description - The description the widget renders below its label, if any. Only\n * whether it is a non-empty string is read.\n * @defaultValue `options.description`: `undefined`, no description is rendered.\n * @returns The props for the control, the ids for the description and error elements, whether an\n * error is due, and the error texts.\n *\n * @example\n * ```tsx\n * const TextField = ({ field, label, description }: FieldOf<string> & { label: string; description?: string }) => {\n * const { control, descriptionId, errorId, isErrorDue, errors } = fieldParts(field, { description })\n *\n * return (\n * <div>\n * <label htmlFor={control.id}>{label}</label>\n * {description && <p id={descriptionId}>{description}</p>}\n * <input {...control} value={field.state.value} onChange={(event) => field.handleChange(event.target.value)} />\n * {isErrorDue && errors.length > 0 && <p id={errorId}>{errors.join(' ')}</p>}\n * </div>\n * )\n * }\n * ```\n */\nexport function fieldParts<TValue>(\n field: FieldApiOf<TValue>,\n options: { description?: string | undefined } = {},\n): FieldParts {\n const id = fieldId(field.form.formId, field.name)\n const descriptionId = `${id}${DESCRIPTION_SUFFIX}`\n const errorId = `${id}${ERROR_SUFFIX}`\n const isErrorDue = isErrorDueFor(field)\n const errors = errorsOf(field)\n const isErrorNamed = isErrorDue && errors.length > 0\n const hasDescription = options.description !== undefined && options.description !== ''\n\n return {\n control: {\n id,\n name: field.name,\n onBlur: field.handleBlur,\n 'aria-invalid': isErrorDue || undefined,\n 'aria-describedby': idrefsOf([\n hasDescription ? descriptionId : undefined,\n isErrorNamed ? errorId : undefined,\n ]),\n 'aria-errormessage': isErrorNamed ? errorId : undefined,\n },\n descriptionId,\n errorId,\n isErrorDue,\n errors,\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/widget/declares-empty-value.ts","../src/render/array-entry.tsx","../src/render/by-name.ts","../src/render/child-path.ts","../src/render/derived.tsx","../src/render/path-pattern.ts","../src/render/required-paths.ts","../src/render/split-entry.ts","../src/render/warn-once.ts","../src/render/fields.tsx","../src/form/find-required-paths.ts","../src/form/blur-then-change.ts","../src/form/form-options-for.ts","../src/form/create-form.tsx","../src/registry/create-registry.ts","../src/widget/errors-of.ts","../src/widget/field-id.ts","../src/widget/field-parts.ts","../src/widget/use-field-parts.ts"],"sourcesContent":["/** Whether `widget` declares its empty value, the value it holds while nothing is entered. */\nexport function declaresEmptyValue(widget: object): widget is { emptyValue: unknown } {\n return Object.hasOwn(widget, 'emptyValue')\n}\n","import { type AnyFieldApi, useSelector } from '@tanstack/react-form'\nimport { type ComponentType, Fragment, type ReactNode } from 'react'\n\n// biome-ignore lint/suspicious/noExplicitAny: a registered frame takes props of its own; the config types check them.\ntype AnyProps = any\n\n/** The keys of an array entry the renderer reads besides its item. */\ntype ArrayOptions = {\n newItem?: unknown\n addLabel?: unknown\n removeLabel?: unknown\n itemKey?: unknown\n}\n\ntype ArrayEntryProps = {\n /** The array field, mounted at the entry's path. */\n field: AnyFieldApi\n options: ArrayOptions\n /** The registered `structure.arrayFrame`, if any. */\n frame: ComponentType<AnyProps> | undefined\n /** The entry's props for the frame. */\n props: object | undefined\n renderItem: (index: number, extra?: object) => ReactNode\n}\n\ntype ItemFunction<TResult> = (item: unknown, index?: number) => TResult\n\n/** What the controls of an array entry show: one key and one remove label per item. */\ntype ItemControls = {\n keys: (string | number)[]\n removeLabels: string[]\n}\n\nconst removeLabelOf = (removeLabel: unknown, item: unknown, index: number): string =>\n typeof removeLabel === 'function'\n ? (removeLabel as ItemFunction<string>)(item, index)\n : String(removeLabel)\n\nconst itemControlsOf = (options: ArrayOptions, value: unknown): ItemControls => {\n const items: unknown[] = Array.isArray(value) ? value : []\n\n return {\n keys: items.map((item, index) =>\n typeof options.itemKey === 'function'\n ? (options.itemKey as ItemFunction<string | number>)(item)\n : index,\n ),\n removeLabels: items.map((item, index) => removeLabelOf(options.removeLabel, item, index)),\n }\n}\n\nconst isSameList = <TItem,>(first: readonly TItem[], second: readonly TItem[]): boolean =>\n first.length === second.length && first.every((item, index) => item === second[index])\n\nconst isSameControls = (first: ItemControls, second: ItemControls): boolean =>\n isSameList(first.keys, second.keys) && isSameList(first.removeLabels, second.removeLabels)\n\n/**\n * Renders an array entry: through the registered frame, which receives `ArrayFrameProps`, or as\n * the bare default, each item followed by its remove button and one add button at the end.\n */\nexport const ArrayEntry = ({\n field,\n options,\n frame: Frame,\n props,\n renderItem,\n}: ArrayEntryProps): ReactNode => {\n // The field renders in TanStack's array mode, so only a change to the list reaches it; keys and\n // labels that name an item follow the items through a selection of their own.\n const { keys, removeLabels } = useSelector(\n field.store,\n (state: { value: unknown }) => itemControlsOf(options, state.value),\n { compare: isSameControls },\n )\n const newItem = options.newItem as () => unknown\n const addLabel = String(options.addLabel)\n const add = (): void => field.pushValue(newItem())\n const remove = (index: number): void => field.removeValue(index)\n\n if (Frame === undefined) {\n return (\n <>\n {keys.map((key, index) => (\n <Fragment key={key}>\n {renderItem(index)}\n <button type=\"button\" data-formdef-remove=\"\" onClick={() => remove(index)}>\n {removeLabels[index]}\n </button>\n </Fragment>\n ))}\n <button type=\"button\" data-formdef-add=\"\" onClick={add}>\n {addLabel}\n </button>\n </>\n )\n }\n\n return (\n <Frame\n {...props}\n field={field}\n keys={keys}\n renderItem={renderItem}\n addLabel={addLabel}\n removeLabels={removeLabels}\n add={add}\n remove={remove}\n insert={(index: number, item?: unknown) =>\n field.insertValue(index, item === undefined ? newItem() : item)\n }\n move={(from: number, to: number) => field.moveValue(from, to)}\n swap={(first: number, second: number) => field.swapValues(first, second)}\n replace={(index: number, item: unknown) => field.replaceValue(index, item)}\n clear={() => field.clearValues()}\n />\n )\n}\n","/** What `map` holds under `name` itself; a name every object inherits finds nothing. */\nexport function byName<TValue>(\n map: Readonly<Record<string, TValue>>,\n name: string,\n): TValue | undefined {\n return Object.hasOwn(map, name) ? map[name] : undefined\n}\n","/**\n * The path of `key` below `parent`: a key of the root level by itself, a key of a nested level\n * after a dot, the index of an array item in brackets.\n */\nexport function childPath(parent: string | undefined, key: string | number): string {\n if (typeof key === 'number') {\n return `${parent ?? ''}[${key}]`\n }\n\n return parent === undefined ? key : `${parent}.${key}`\n}\n","import { getBy } from '@tanstack/react-form'\nimport { type ReactNode, useRef } from 'react'\n\nimport type { RenderForm } from './render-form'\n\n/** What an entry reads from the form values: its condition, its props and the paths they read. */\ntype DerivedEntry = {\n visibleWhen?: unknown\n props?: unknown\n dependsOn?: unknown\n}\n\ntype DerivedProps = {\n form: RenderForm\n entry: DerivedEntry\n /** Builds the entry from its props; called only while the entry is visible. */\n children: (props: object | undefined) => ReactNode\n}\n\ntype LastRead = {\n entry: DerivedEntry\n dependencies: unknown[]\n selected: unknown\n}\n\nconst HIDDEN = Symbol('hidden')\nconst VISIBLE = Symbol('visible')\n\nconst propsOf = (props: unknown): object | undefined =>\n typeof props === 'object' && props !== null ? props : undefined\n\nconst isStale = (\n last: LastRead | undefined,\n entry: DerivedEntry,\n dependencies: unknown[],\n): boolean =>\n last === undefined ||\n last.entry !== entry ||\n dependencies.some((value, index) => !Object.is(value, last.dependencies[index]))\n\n/**\n * Renders an entry from what it reads from the form values. An entry with a condition or derived\n * props subscribes to the values; the selected result alone decides whether it re-renders: whether\n * it is shown for a condition, the values for derived props. `dependsOn` narrows when the selector\n * reads again, never what it returns. A hidden entry is never built.\n */\nexport const Derived = ({ form, entry, children }: DerivedProps): ReactNode => {\n const lastRead = useRef<LastRead | undefined>(undefined)\n const { visibleWhen, props, dependsOn } = entry\n const isConditional = typeof visibleWhen === 'function'\n const isDerived = typeof props === 'function'\n\n if (!(isConditional || isDerived)) {\n return children(propsOf(props))\n }\n\n const selectFrom = (values: unknown): unknown => {\n if (isConditional && !visibleWhen(values)) {\n return HIDDEN\n }\n\n return isDerived ? values : VISIBLE\n }\n\n const select = ({ values }: { values: unknown }): unknown => {\n if (!Array.isArray(dependsOn)) {\n return selectFrom(values)\n }\n\n const dependencies = dependsOn.map((path: string) => getBy(values, path))\n if (isStale(lastRead.current, entry, dependencies)) {\n lastRead.current = { entry, dependencies, selected: selectFrom(values) }\n }\n\n return lastRead.current?.selected\n }\n\n return (\n <form.Subscribe selector={select}>\n {(selected) => {\n if (selected === HIDDEN) {\n return null\n }\n\n return children(propsOf(isDerived ? props(selected) : props))\n }}\n </form.Subscribe>\n )\n}\n","const ITEM_INDEX = /\\[\\d+\\]/g\nconst ANY_ITEM = '[]'\n\n/** The path with every array index left open: `contacts[2].name` becomes `contacts[].name`. */\nexport function pathPattern(path: string): string {\n return path.replaceAll(ITEM_INDEX, ANY_ITEM)\n}\n","import { type Context, createContext } from 'react'\n\nexport const NO_REQUIRED_PATHS: ReadonlySet<string> = new Set()\n\n/** The path patterns of the required fields of the level being rendered. */\nexport const RequiredPaths: Context<ReadonlySet<string>> = createContext(NO_REQUIRED_PATHS)\n","/**\n * The keys formdef reads from a field entry. Every other key is one of TanStack's field options and\n * goes to the field unchanged, so an option TanStack adds works without a formdef release.\n */\nexport const OWN_ENTRY_KEYS = [\n 'widget',\n 'props',\n 'fields',\n 'layout',\n 'item',\n 'newItem',\n 'addLabel',\n 'removeLabel',\n 'itemKey',\n 'render',\n 'visibleWhen',\n 'dependsOn',\n] as const\n\n/** A key formdef reads from a field entry. */\nexport type OwnEntryKey = (typeof OWN_ENTRY_KEYS)[number]\n\nconst OWN_ENTRY_KEY_SET: ReadonlySet<string> = new Set(OWN_ENTRY_KEYS)\n\n/** Splits a field entry into the keys formdef reads and TanStack's field options. */\nexport function splitEntry(entry: object): {\n own: Partial<Record<OwnEntryKey, unknown>>\n fieldOptions: Record<string, unknown>\n} {\n const entries = Object.entries(entry)\n\n // `fromEntries` defines each key as data, so a key named `__proto__` cannot set a prototype.\n return {\n own: Object.fromEntries(entries.filter(([key]) => OWN_ENTRY_KEY_SET.has(key))),\n fieldOptions: Object.fromEntries(entries.filter(([key]) => !OWN_ENTRY_KEY_SET.has(key))),\n }\n}\n","// Bundlers replace `process.env.NODE_ENV` in exactly this spelling; without one, `process` may not\n// exist at all.\n// biome-ignore lint/style/useNamingConvention: the name bundlers replace.\ndeclare const process: { env: { NODE_ENV?: string } } | undefined\n\nconst warnedOwners = new WeakSet<object>()\n\nconst isProduction = (): boolean =>\n typeof process !== 'undefined' && process.env.NODE_ENV === 'production'\n\n/** Warns once per `owner`, and never in production. */\nexport function warnOnce(owner: object, message: string): void {\n if (isProduction() || warnedOwners.has(owner)) {\n return\n }\n\n warnedOwners.add(owner)\n console.warn(message)\n}\n","import { type ComponentType, type ReactNode, use } from 'react'\n\nimport { declaresEmptyValue } from '../widget/declares-empty-value'\n\nimport { ArrayEntry } from './array-entry'\nimport { byName } from './by-name'\nimport { childPath } from './child-path'\nimport { Derived } from './derived'\nimport { pathPattern } from './path-pattern'\nimport type { AppFieldProps, RenderForm } from './render-form'\nimport { NO_REQUIRED_PATHS, RequiredPaths } from './required-paths'\nimport { splitEntry } from './split-entry'\nimport { warnOnce } from './warn-once'\n\n// biome-ignore lint/suspicious/noExplicitAny: registered components take props of their own; the config types check them.\ntype AnyProps = any\n\ntype ComponentMap = Readonly<Record<string, ComponentType<AnyProps>>>\n\n/** The maps of a registry the renderer looks names up in. */\ntype RenderParts = {\n widgets: ComponentMap\n formComponents: ComponentMap\n structure: {\n section?: ComponentType<AnyProps>\n row?: ComponentType<AnyProps>\n arrayFrame?: ComponentType<AnyProps>\n }\n}\n\ntype LevelFields = Readonly<Record<string, object>>\n\ntype LayoutNodeEntry = string | Readonly<Record<string, unknown>>\n\n/** The props of {@link Fields}. */\nexport type FieldsProps = {\n parts: RenderParts\n form: RenderForm\n fields: LevelFields\n layout?: readonly unknown[] | undefined\n /** The path patterns of the required fields; none unless `createForm` renders the level. */\n requiredPaths?: ReadonlySet<string> | undefined\n}\n\ntype Level = Omit<FieldsProps, 'layout'> & {\n path: string | undefined\n /** Props an array frame passes the one widget of an item, through this level. */\n extra?: object | undefined\n}\n\ntype LevelProps = Level & {\n layout: readonly unknown[] | undefined\n}\n\ntype EntryProps = Omit<Level, 'fields'> & {\n entry: object\n path: string\n}\n\ntype FieldContentProps = Omit<EntryProps, 'entry'> & {\n entry: ReturnType<typeof splitEntry> & { source: object }\n props: object | undefined\n}\n\ntype ItemProps = Omit<EntryProps, 'extra'> & {\n index: number\n extra: object | undefined\n}\n\ntype LayoutNodeProps = {\n level: Level\n entry: LayoutNodeEntry\n}\n\ntype LayoutKindProps = {\n level: Level\n entry: Readonly<Record<string, unknown>>\n props: object | undefined\n}\n\nconst ARRAY_MODE = 'array'\n\nconst DROPPED_EXTRA = 'passes extra props to an item that has no single widget to receive them'\n\nconst renderNothing = (): ReactNode => null\n\nconst layoutOwner = (path: string | undefined): string =>\n path === undefined ? 'the form' : `\"${path}\"`\n\nconst isLeaf = (entry: unknown): boolean =>\n typeof entry === 'object' && entry !== null && 'widget' in entry\n\n// A frame's extra props have a single target only in a leaf, or an object level whose one field\n// is a leaf and which has no layout.\nconst takesExtra = (item: object): boolean => {\n if (isLeaf(item)) {\n return true\n }\n const { own } = splitEntry(item)\n const fields =\n typeof own.fields === 'object' && own.fields !== null ? Object.values(own.fields) : []\n\n return own.layout === undefined && fields.length === 1 && isLeaf(fields[0])\n}\n\nconst Item = ({ parts, form, entry, path, index, extra }: ItemProps): ReactNode => {\n const { own } = splitEntry(entry)\n const item = own.item as object\n const reachesWidget = extra !== undefined && takesExtra(item)\n if (extra !== undefined && !reachesWidget) {\n warnOnce(entry, `formdef: the array frame of \"${path}\" ${DROPPED_EXTRA}; they are dropped.`)\n }\n\n return (\n <Entry\n parts={parts}\n form={form}\n entry={item}\n path={childPath(path, index)}\n extra={reachesWidget ? extra : undefined}\n />\n )\n}\n\nconst FieldContent = ({ parts, form, entry, path, props, extra }: FieldContentProps): ReactNode => {\n const { own, fieldOptions } = entry\n const requiredPaths = use(RequiredPaths)\n\n if (typeof own.widget === 'string') {\n const Widget = byName(parts.widgets, own.widget)\n if (Widget === undefined) {\n throw new Error(`formdef: field \"${path}\" names the unregistered widget \"${own.widget}\".`)\n }\n\n // The config's `required` comes after the found one, so it wins.\n const found = declaresEmptyValue(Widget)\n ? { required: requiredPaths.has(pathPattern(path)) }\n : undefined\n\n return (\n <form.AppField {...fieldOptions} name={path}>\n {(field) => <Widget {...found} {...props} {...extra} field={field} />}\n </form.AppField>\n )\n }\n\n if (typeof own.render === 'function') {\n return (\n <form.AppField {...fieldOptions} name={path}>\n {own.render as AppFieldProps['children']}\n </form.AppField>\n )\n }\n\n if (typeof own.item === 'object' && own.item !== null) {\n return (\n <form.AppField {...fieldOptions} name={path} mode={ARRAY_MODE}>\n {(field) => (\n <ArrayEntry\n field={field}\n options={own}\n frame={parts.structure.arrayFrame}\n props={props}\n renderItem={(index, itemExtra) => (\n <Item\n parts={parts}\n form={form}\n entry={entry.source}\n path={path}\n index={index}\n extra={itemExtra}\n />\n )}\n />\n )}\n </form.AppField>\n )\n }\n\n if (typeof own.fields !== 'object' || own.fields === null) {\n return null\n }\n\n return (\n <>\n {Object.keys(fieldOptions).length > 0 && (\n <form.AppField {...fieldOptions} name={path}>\n {renderNothing}\n </form.AppField>\n )}\n <LevelEntries\n parts={parts}\n form={form}\n fields={own.fields as LevelFields}\n layout={Array.isArray(own.layout) ? own.layout : undefined}\n path={path}\n extra={extra}\n />\n </>\n )\n}\n\nconst Entry = ({ parts, form, entry, path, extra }: EntryProps): ReactNode => {\n const split = { ...splitEntry(entry), source: entry }\n\n return (\n <Derived form={form} entry={split.own}>\n {(props) => (\n <FieldContent\n parts={parts}\n form={form}\n entry={split}\n path={path}\n props={props}\n extra={extra}\n />\n )}\n </Derived>\n )\n}\n\nconst FieldNode = ({ level, name }: { level: Level; name: string }): ReactNode => {\n const entry = byName(level.fields, name)\n if (entry === undefined) {\n throw new Error(\n `formdef: the layout of ${layoutOwner(level.path)} names \"${name}\", which the level does not describe.`,\n )\n }\n\n return (\n <Entry\n parts={level.parts}\n form={level.form}\n entry={entry}\n path={childPath(level.path, name)}\n extra={level.extra}\n />\n )\n}\n\nconst LayoutNodes = ({ level, entries }: { level: Level; entries: unknown }): ReactNode =>\n (Array.isArray(entries) ? (entries as LayoutNodeEntry[]) : []).map((entry, index) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: a layout comes from the config and never reorders; a name may appear twice.\n <LayoutNode key={index} level={level} entry={entry} />\n ))\n\nconst SectionNode = ({ level, entry, props }: LayoutKindProps): ReactNode => {\n const children = <LayoutNodes level={level} entries={entry.layout} />\n const Section = level.parts.structure.section\n\n if (Section !== undefined) {\n return (\n <Section {...props} label={entry.section}>\n {children}\n </Section>\n )\n }\n\n return (\n <fieldset {...props}>\n <legend>{entry.section as string}</legend>\n {children}\n </fieldset>\n )\n}\n\nconst RowNode = ({ level, entry, props }: LayoutKindProps): ReactNode => {\n const children = <LayoutNodes level={level} entries={entry.row} />\n const Row = level.parts.structure.row\n\n if (Row !== undefined) {\n return <Row {...props}>{children}</Row>\n }\n\n return (\n <div {...props} data-formdef-row=\"\">\n {children}\n </div>\n )\n}\n\nconst ComponentNode = ({ level, entry, props }: LayoutKindProps): ReactNode => {\n const name = entry.component as string\n const Component = byName(level.parts.formComponents, name)\n if (Component === undefined) {\n throw new Error(\n `formdef: the layout of ${layoutOwner(level.path)} places the unregistered form component \"${name}\".`,\n )\n }\n\n return <Component {...props} />\n}\n\nconst LayoutKind = ({ level, entry, props }: LayoutKindProps): ReactNode => {\n if ('section' in entry) {\n return <SectionNode level={level} entry={entry} props={props} />\n }\n if ('row' in entry) {\n return <RowNode level={level} entry={entry} props={props} />\n }\n if (typeof entry.component === 'string') {\n return <ComponentNode level={level} entry={entry} props={props} />\n }\n if (typeof entry.render === 'function') {\n return (entry.render as (form: RenderForm) => ReactNode)(level.form)\n }\n\n return null\n}\n\nconst LayoutNode = ({ level, entry }: LayoutNodeProps): ReactNode => {\n if (typeof entry === 'string') {\n return <FieldNode level={level} name={entry} />\n }\n\n return (\n <Derived form={level.form} entry={entry}>\n {(props) => <LayoutKind level={level} entry={entry} props={props} />}\n </Derived>\n )\n}\n\nconst LevelEntries = ({ layout, ...level }: LevelProps): ReactNode => {\n if (layout !== undefined) {\n return <LayoutNodes level={level} entries={layout} />\n }\n\n return Object.keys(level.fields).map((name) => <FieldNode key={name} level={level} name={name} />)\n}\n\n/**\n * Renders the field entries of a level with the widgets of `parts`. Without a `layout` the entries\n * appear in the order `fields` declares them; with one, the layout decides the order, groups them\n * into sections and rows and places form components and inline entries. A leaf renders as its\n * widget, an object as its own level, a custom entry through its `render`, an array through the\n * registered `structure.arrayFrame` or the bare default. An object entry with TanStack options of\n * its own also mounts a field at its path, so its validators run. A `Fields` nested in a form finds\n * no required fields of its own, since its paths may be relative to a field group.\n */\nexport const Fields = ({ parts, form, fields, layout, requiredPaths }: FieldsProps): ReactNode => (\n <RequiredPaths value={requiredPaths ?? NO_REQUIRED_PATHS}>\n <LevelEntries parts={parts} form={form} fields={fields} layout={layout} path={undefined} />\n </RequiredPaths>\n)\n","import type { StandardSchemaV1 } from '@tanstack/react-form'\n\nimport { byName } from '../render/by-name'\nimport { childPath } from '../render/child-path'\nimport { pathPattern } from '../render/path-pattern'\nimport { splitEntry } from '../render/split-entry'\nimport { declaresEmptyValue } from '../widget/declares-empty-value'\n\ntype LevelFields = Readonly<Record<string, object>>\n\ntype Widgets = Readonly<Record<string, object>>\n\ntype RequiredPathsInput = {\n schema: unknown\n /** Whether the config validates the schema asynchronously (`validation.async`). */\n validatesAsync: boolean\n fields: LevelFields\n widgets: Widgets\n}\n\n/** The path patterns of the required fields, or why none could be found. */\ntype RequiredPathsResult = { paths: ReadonlySet<string> } | { problem: string }\n\ntype EmptyValues = {\n widgets: Widgets\n /** The path patterns of the leaves whose widget declares an empty value. */\n declared: Set<string>\n}\n\ntype Validation = ReturnType<StandardSchemaV1['~standard']['validate']>\ntype Issue = NonNullable<Exclude<Validation, Promise<unknown>>['issues']>[number]\n\nconst NONE: RequiredPathsResult = { paths: new Set() }\n\nconst ASYNC_SCHEMA =\n 'formdef: the schema validates asynchronously, so no field is found to be required; pass required in props.'\nconst UNSETTLED_SCHEMA =\n 'formdef: the schema did not validate empty values synchronously, so no field is found to be required; pass required in props.'\nconst THROWING_SCHEMA =\n 'formdef: the schema threw on empty values, so no field is found to be required'\n\nconst FIRST_ITEM = 0\n\nconst isStandardSchema = (schema: unknown): schema is StandardSchemaV1 =>\n typeof schema === 'object' && schema !== null && '~standard' in schema\n\nconst emptyLevel = (fields: LevelFields, path: string | undefined, empty: EmptyValues): object =>\n Object.fromEntries(\n Object.entries(fields).map(([key, entry]) => [\n key,\n emptyEntry(entry, childPath(path, key), empty),\n ]),\n )\n\nfunction emptyEntry(entry: object, path: string, empty: EmptyValues): unknown {\n const { own } = splitEntry(entry)\n\n if (typeof own.widget === 'string') {\n const widget = byName(empty.widgets, own.widget)\n if (widget === undefined || !declaresEmptyValue(widget)) {\n return undefined\n }\n empty.declared.add(pathPattern(path))\n\n return widget.emptyValue\n }\n if (typeof own.item === 'object' && own.item !== null) {\n return [emptyEntry(own.item, childPath(path, FIRST_ITEM), empty)]\n }\n if (typeof own.fields === 'object' && own.fields !== null) {\n return emptyLevel(own.fields as LevelFields, path, empty)\n }\n\n return undefined\n}\n\nconst issuePath = (issue: Issue): string =>\n pathPattern(\n (issue.path ?? []).reduce<string | undefined>((parent, segment) => {\n const key = typeof segment === 'object' ? segment.key : segment\n\n return childPath(parent, typeof key === 'number' ? key : String(key))\n }, undefined) ?? '',\n )\n\nconst parentOf = (path: string): string =>\n path.slice(0, Math.max(path.lastIndexOf('.'), path.lastIndexOf('[')))\n\n// A leaf's value may be an object or a list, so an issue below a leaf's path is the leaf's.\nconst leafOf = (path: string, leaves: ReadonlySet<string>): string | undefined => {\n for (let candidate = path; candidate !== ''; candidate = parentOf(candidate)) {\n if (leaves.has(candidate)) {\n return candidate\n }\n }\n\n return undefined\n}\n\n// The promise is dropped unread; a rejection is the schema failing on empty values, not the form.\nconst ignoreRejection = (): undefined => undefined\n\nconst errorText = (error: unknown): string =>\n error instanceof Error ? error.message : String(error)\n\n/**\n * Finds the required fields of a config: validates `schema` once, synchronously, with every leaf\n * holding its widget's empty value and every array one such item. A leaf whose widget declares an\n * empty value and whose path receives an issue is required. A schema the config validates\n * asynchronously is not run, so its asynchronous checks send nothing.\n */\nexport function findRequiredPaths({\n schema,\n validatesAsync,\n fields,\n widgets,\n}: RequiredPathsInput): RequiredPathsResult {\n if (!isStandardSchema(schema)) {\n return NONE\n }\n if (validatesAsync) {\n return { problem: ASYNC_SCHEMA }\n }\n\n const empty: EmptyValues = { widgets, declared: new Set() }\n const values = emptyLevel(fields, undefined, empty)\n let result: Validation\n try {\n result = schema['~standard'].validate(values)\n } catch (error) {\n return { problem: `${THROWING_SCHEMA}: ${errorText(error)}` }\n }\n if (result instanceof Promise) {\n result.then(undefined, ignoreRejection)\n\n return { problem: UNSETTLED_SCHEMA }\n }\n\n const leaves = (result.issues ?? []).map((issue) => leafOf(issuePath(issue), empty.declared))\n\n return { paths: new Set(leaves.filter((leaf) => leaf !== undefined)) }\n}\n","import {\n defaultValidationLogic,\n type ValidationLogicFn,\n type ValidationLogicProps,\n} from '@tanstack/react-form'\n\ntype ScheduledValidators = Parameters<ValidationLogicProps['runValidation']>[0]['validators']\n\n// Gated on the form, not the changed field: the schema validates the whole form, and TanStack names\n// the changed field only on the field-level call, where the schema does not sit.\nconst isSchemaDue = ({ event, form }: ValidationLogicProps): boolean => {\n if (event.type === 'blur' || event.type === 'submit') {\n return true\n }\n\n return event.type === 'change' && !form.state.isValid\n}\n\n/**\n * The validation logic of the `blurThenChange` mode: TanStack's default logic, plus the schema in\n * `onDynamic` when a field is left, on submit, and on every change while the form is invalid.\n */\nexport const blurThenChange: ValidationLogicFn = (props) => {\n let defaults: ScheduledValidators = []\n defaultValidationLogic({\n ...props,\n runValidation: ({ validators }) => {\n defaults = validators\n },\n })\n\n const schema = props.event.async ? props.validators?.onDynamicAsync : props.validators?.onDynamic\n if (schema === undefined || !isSchemaDue(props)) {\n return props.runValidation({ validators: defaults, form: props.form })\n }\n\n return props.runValidation({\n validators: [...defaults, { fn: schema, cause: 'dynamic' }],\n form: props.form,\n })\n}\n","import { revalidateLogic, type ValidationLogicFn } from '@tanstack/react-form'\n\nimport type { Validation } from '../config/validation'\nimport { blurThenChange } from './blur-then-change'\n\ntype Mode = NonNullable<Validation['mode']>\ntype DynamicMode = 'revalidate' | 'blurThenChange'\ntype Slot = 'onDynamic' | 'onChange' | 'onBlur' | 'onSubmit'\n\ntype Options = Readonly<Record<string, unknown>>\n\ntype FormOptionsInput = {\n /** The form config: the schema, formdef's own keys and TanStack's form options. */\n config: Options\n /** The instance's TanStack form options, from the form component's `options` prop. */\n options: Options | undefined\n}\n\nconst DEFAULT_MODE: Mode = 'revalidate'\n\nconst isDynamic = (mode: Mode): mode is DynamicMode =>\n mode === 'revalidate' || mode === 'blurThenChange'\n\nconst logicFor = (mode: DynamicMode): ValidationLogicFn =>\n mode === 'revalidate' ? revalidateLogic() : blurThenChange\n\nconst slotFor = (mode: Mode, isAsync: boolean): Slot | `${Slot}Async` => {\n const slot: Slot = isDynamic(mode) ? 'onDynamic' : mode\n\n return isAsync ? `${slot}Async` : slot\n}\n\nconst recordOf = (value: unknown): Options =>\n typeof value === 'object' && value !== null ? (value as Options) : {}\n\n// An instance passing `undefined`, such as data still loading, leaves the config's value in place.\nconst setEntriesOf = (value: unknown): Options =>\n Object.fromEntries(Object.entries(recordOf(value)).filter(([, entry]) => entry !== undefined))\n\n/**\n * The TanStack form options of a form: the config's options and the instance's merged key by key,\n * the instance winning, except `validators` and `listeners`, which merge slot by slot; a key or\n * slot the instance sets to `undefined` counts as unset. The schema\n * goes into the slot `validation` picks unless a validator already fills it; a dynamic mode brings\n * its validation logic, an asynchronous schema its debounce unless the options set one.\n */\nexport function formOptionsFor({ config, options }: FormOptionsInput): Record<string, unknown> {\n const {\n schema,\n validation,\n fields: _renderedByFields,\n layout: _arrangedByFields,\n ...configOptions\n } = config\n const { mode = DEFAULT_MODE, ...timing } = recordOf(validation) as Validation\n const isAsync = timing.async === true\n const slot = slotFor(mode, isAsync)\n const instanceOptions = setEntriesOf(options)\n const validators = {\n ...recordOf(configOptions.validators),\n ...setEntriesOf(instanceOptions.validators),\n }\n\n return {\n ...(isDynamic(mode) && { validationLogic: logicFor(mode) }),\n ...(isAsync &&\n timing.asyncDebounceMs !== undefined && { asyncDebounceMs: timing.asyncDebounceMs }),\n ...configOptions,\n ...instanceOptions,\n validators: validators[slot] === undefined ? { ...validators, [slot]: schema } : validators,\n listeners: { ...recordOf(configOptions.listeners), ...setEntriesOf(instanceOptions.listeners) },\n }\n}\n","import type { ComponentType, FormEvent, ReactNode } from 'react'\n\nimport type { FormElementProps } from '../config/form-props'\nimport type { BodyProps } from '../config/structure'\nimport { Fields, type FieldsProps } from '../render/fields'\nimport type { RenderForm } from '../render/render-form'\nimport { warnOnce } from '../render/warn-once'\nimport { findRequiredPaths } from './find-required-paths'\nimport { formOptionsFor } from './form-options-for'\n\ntype Options = Readonly<Record<string, unknown>>\n\n/** The form `useAppForm` returns, as far as the shell uses it. */\ntype ShellForm = RenderForm & {\n AppForm: ComponentType<{ children?: ReactNode }>\n handleSubmit: () => Promise<void>\n}\n\n/** What a registry binds a form to: its form hook and the maps the renderer looks names up in. */\nexport type FormBinding = {\n useAppForm: (options: Options) => ShellForm\n parts: FieldsProps['parts'] & {\n structure: { body?: ComponentType<BodyProps> }\n }\n}\n\n/** The props of a form component: the instance's options and what a `<form>` element takes. */\ntype FormComponentProps = FormElementProps & {\n options?: Options\n}\n\n/** A form config: TanStack's form options plus formdef's own keys. */\ntype ConfigInput = Options & {\n fields?: unknown\n layout?: unknown\n}\n\ntype ConfiguredFormProps = FormComponentProps & {\n binding: FormBinding\n config: ConfigInput\n requiredPaths: ReadonlySet<string>\n}\n\nconst MISSING_DEFAULT_VALUES =\n 'formdef: the form has no default values; set defaultValues in its config or in its options.'\n\nconst ConfiguredForm = ({\n binding,\n config,\n requiredPaths,\n options,\n ...formProps\n}: ConfiguredFormProps): ReactNode => {\n const formOptions = formOptionsFor({ config, options })\n if (formOptions.defaultValues === undefined) {\n throw new Error(MISSING_DEFAULT_VALUES)\n }\n\n const form = binding.useAppForm(formOptions)\n const Body = binding.parts.structure.body\n const content = (\n <Fields\n parts={binding.parts}\n form={form}\n fields={config.fields as FieldsProps['fields']}\n layout={config.layout as FieldsProps['layout']}\n requiredPaths={requiredPaths}\n />\n )\n const handleSubmit = (event: FormEvent<HTMLFormElement>): void => {\n event.preventDefault()\n event.stopPropagation()\n void form.handleSubmit()\n }\n\n return (\n <form.AppForm>\n <form noValidate {...formProps} onSubmit={handleSubmit}>\n {Body === undefined ? content : <Body>{content}</Body>}\n </form>\n </form.AppForm>\n )\n}\n\nconst isAsyncValidation = (validation: unknown): boolean =>\n typeof validation === 'object' && validation !== null && 'async' in validation\n ? validation.async === true\n : false\n\nconst requiredPathsOf = (binding: FormBinding, config: ConfigInput): ReadonlySet<string> => {\n const result = findRequiredPaths({\n schema: config.schema,\n validatesAsync: isAsyncValidation(config.validation),\n fields: (config.fields ?? {}) as Readonly<Record<string, object>>,\n widgets: binding.parts.widgets,\n })\n if ('problem' in result) {\n warnOnce(config, result.problem)\n\n return new Set()\n }\n\n return result.paths\n}\n\n/**\n * Turns a form config into a form component bound to `binding`: TanStack's form with the merged\n * options, a `<form>` element that submits through it, the registered body and the config's fields.\n * When it first renders, it finds the config's required fields once.\n */\nexport function createForm(\n binding: FormBinding,\n config: ConfigInput,\n): (props: FormComponentProps) => ReactNode {\n let requiredPaths: ReadonlySet<string> | undefined\n\n return (props: FormComponentProps): ReactNode => {\n requiredPaths ??= requiredPathsOf(binding, config)\n\n return (\n <ConfiguredForm {...props} binding={binding} config={config} requiredPaths={requiredPaths} />\n )\n }\n}\n","import { type AnyFieldApi, type AnyFormApi, createFormHook } from '@tanstack/react-form'\nimport { type Context, createElement, type ReactNode } from 'react'\n\nimport { createForm, type FormBinding } from '../form/create-form'\nimport { Fields, type FieldsProps } from '../render/fields'\n\nimport type {\n CheckedStructure,\n CheckedWidgets,\n Components,\n NewFormComponents,\n NewStructure,\n NewWidgets,\n Registry,\n Structure,\n} from './registry'\n\ntype Contexts = {\n fieldContext: Context<AnyFieldApi>\n formContext: Context<AnyFormApi>\n}\n\ntype RegistryParts<TWidgets, TFormComponents, TStructure> = Contexts & {\n widgets: TWidgets\n formComponents: TFormComponents\n structure: TStructure\n}\n\nfunction buildRegistry<\n TWidgets extends Components,\n TFormComponents extends Components,\n TStructure extends Structure,\n>(\n parts: RegistryParts<TWidgets, TFormComponents, TStructure>,\n): Registry<TWidgets, TFormComponents, TStructure> {\n const { extendForm: _replacedByExtendRegistry, ...formHook } = createFormHook({\n fieldContext: parts.fieldContext,\n formContext: parts.formContext,\n fieldComponents: parts.widgets,\n formComponents: parts.formComponents,\n })\n\n function extendRegistry<\n const TNewWidgets extends NewWidgets<TWidgets> = Record<never, never>,\n const TNewFormComponents extends NewFormComponents<TFormComponents> = Record<never, never>,\n const TNewStructure extends NewStructure<TStructure> = Record<never, never>,\n >(extension: {\n widgets?: TNewWidgets\n formComponents?: TNewFormComponents\n structure?: TNewStructure & CheckedStructure<TNewStructure>\n }): Registry<\n TWidgets & TNewWidgets,\n TFormComponents & TNewFormComponents,\n TStructure & TNewStructure\n > {\n const addedWidgets: Components = extension.widgets ?? {}\n const addedStructure: Structure = extension.structure ?? {}\n\n // The extension's maps are optional, and spreading an optional generic map drops its type.\n return buildRegistry({\n fieldContext: parts.fieldContext,\n formContext: parts.formContext,\n widgets: { ...parts.widgets, ...addedWidgets } as TWidgets & TNewWidgets,\n formComponents: { ...parts.formComponents, ...extension.formComponents } as TFormComponents &\n TNewFormComponents,\n structure: { ...parts.structure, ...addedStructure } as TStructure & TNewStructure,\n })\n }\n\n // TanStack's hook is generic over the form's types; the config's types are checked at the\n // registry's `createForm`, so the binding passes the merged options on untyped.\n const binding: FormBinding = {\n useAppForm: formHook.useAppForm as unknown as FormBinding['useAppForm'],\n parts: {\n widgets: parts.widgets,\n formComponents: parts.formComponents,\n structure: parts.structure,\n },\n }\n\n // biome-ignore lint/correctness/noComponentHookFactories: created once per registry, like TanStack's createFormHook.\n const BoundFields = (props: Omit<FieldsProps, 'parts'>): ReactNode =>\n createElement(Fields, { ...props, parts: binding.parts })\n\n return {\n formHook,\n createForm: (config) => createForm(binding, config),\n // The member's signature types the level by the form it is given; the renderer reads it untyped.\n Fields: BoundFields as unknown as Registry<TWidgets, TFormComponents, TStructure>['Fields'],\n // The member's signature checks the widgets' empty values; the implementation merges any map.\n extendRegistry: extendRegistry as Registry<\n TWidgets,\n TFormComponents,\n TStructure\n >['extendRegistry'],\n }\n}\n\n/**\n * Binds widgets, form components and structure slots to the app's TanStack contexts and returns a\n * registry, the value every form of the app is built with.\n *\n * Create the contexts with TanStack's `createFormHookContexts()` in a module of their own, so a\n * form component can read the form through `useFormContext()` without importing the registry.\n *\n * @typeParam TWidgets - The widgets by name; each takes its field as a prop typed `FieldOf<V>`.\n * @typeParam TFormComponents - The form components by name, placed in a layout by `{ component }`.\n * @typeParam TStructure - The structure slots: `body`, `section`, `row` and `arrayFrame`.\n * @param config - The contexts and what the registry holds.\n * @param config.fieldContext - The field context from `createFormHookContexts()`.\n * @param config.formContext - The form context from `createFormHookContexts()`.\n * @param config.widgets - The widgets by name. A widget's static `emptyValue`, the value it holds\n * while nothing is entered, must be of the value type it serves.\n * @param config.formComponents - The form components by name.\n * @param config.structure - The components that render the structure slots.\n * @defaultValue `config.formComponents` and `config.structure`: none registered.\n * @returns The registry.\n *\n * @example\n * ```tsx\n * 'use client'\n * import { createRegistry } from '@formdef/core'\n * import { fieldContext, formContext } from './context'\n *\n * export const registry = createRegistry({\n * fieldContext,\n * formContext,\n * widgets: { text: TextField, checkbox: CheckboxField },\n * formComponents: { SubmitButton },\n * structure: { section: Section },\n * })\n * ```\n */\nexport function createRegistry<\n const TWidgets extends Components,\n const TFormComponents extends Components = Record<never, never>,\n const TStructure extends Structure = Record<never, never>,\n>(\n config: Contexts & {\n widgets: TWidgets & CheckedWidgets<TWidgets>\n formComponents?: TFormComponents\n structure?: TStructure & CheckedStructure<TStructure>\n },\n): Registry<TWidgets, TFormComponents, TStructure> {\n // Both maps fall back to their empty defaults exactly when they are left out.\n return buildRegistry<TWidgets, TFormComponents, TStructure>({\n fieldContext: config.fieldContext,\n formContext: config.formContext,\n widgets: config.widgets,\n formComponents: config.formComponents ?? ({} as TFormComponents),\n structure: config.structure ?? ({} as TStructure),\n })\n}\n","import type { FieldApiOf } from './field-of'\n\nfunction textOf(error: unknown): string | undefined {\n if (typeof error === 'string') {\n return error\n }\n if (typeof error !== 'object' || error === null || !('message' in error)) {\n return undefined\n }\n\n return typeof error.message === 'string' ? error.message : undefined\n}\n\nfunction hasText(text: string | undefined): text is string {\n return text !== undefined && text !== ''\n}\n\n/**\n * Turns the errors of a field into the texts a widget renders.\n *\n * An error is either a string or an object with a `message`, such as an issue of a Standard\n * Schema. Errors without text are left out, and a text that several validators report appears\n * once, where it first occurs. A field's errors stay readable with TanStack's `disableErrorFlat`\n * option.\n *\n * @typeParam TValue - The value type of the field.\n * @param field - The field the widget renders.\n * @returns The distinct texts of the field's current errors, in order; empty when there are none.\n *\n * @example\n * ```tsx\n * const TextField = ({ field }: FieldOf<string>) => (\n * <>\n * <input value={field.state.value} onChange={(event) => field.handleChange(event.target.value)} />\n * <p>{errorsOf(field).join(' ')}</p>\n * </>\n * )\n * ```\n */\nexport function errorsOf<TValue>(field: FieldApiOf<TValue>): string[] {\n const texts = field.state.meta.errors.flat().map(textOf).filter(hasText)\n\n return [...new Set(texts)]\n}\n","const PATH_SEPARATORS = /[.[\\]]+/g\nconst SELECTOR_SAFE_SEPARATOR = '__'\n\n/**\n * Builds the DOM id that `useFieldParts` gives the control of a field, for code that has no\n * field at hand — an error summary above the form that links to each invalid field, for example.\n *\n * The id is the form's id, a hyphen, and the field's path with every run of dots and brackets\n * replaced by `__`. Fields of two different forms therefore never share an id, and the id can be\n * used in a CSS selector without escaping.\n *\n * @param formId - The id of the form, as TanStack Form reports it in `form.formId`.\n * @param path - The path of the field, such as `email`, `address.zip` or `contacts[0].name`.\n * @returns The id of the field's control, such as `signup-address__zip`.\n *\n * @remarks\n * The replacement cannot be reversed: a field literally named `a__b` gets the same id as the\n * path `a.b`.\n *\n * @example\n * ```tsx\n * <a href={`#${fieldId(form.formId, 'address.zip')}`}>Postal code</a>\n * ```\n */\nexport function fieldId(formId: string, path: string): string {\n return `${formId}-${path.replace(PATH_SEPARATORS, SELECTOR_SAFE_SEPARATOR)}`\n}\n","import { errorsOf } from './errors-of'\nimport { fieldId } from './field-id'\nimport type { FieldApiOf } from './field-of'\n\nconst DESCRIPTION_SUFFIX = '-description'\nconst ERROR_SUFFIX = '-error'\nconst IDREF_SEPARATOR = ' '\n\n/**\n * What {@link useFieldParts} returns: the wiring of a widget's control and what it needs to render\n * its description and its errors.\n */\nexport interface FieldParts {\n /** Props to spread onto the control; its value and change handler stay with the widget. */\n control: {\n /** The control's id, as {@link fieldId} builds it. */\n id: string\n /** The field's path, so the browser can autofill the control. */\n name: string\n /** Reports to the field that the control was left. */\n onBlur: () => void\n /** `true` while an error is due; absent otherwise, never `false`. */\n 'aria-invalid': true | undefined\n /** The ids of the description and of a due error with text, when there are any. */\n 'aria-describedby': string | undefined\n /** The id of the error element, while an error with text is due. */\n 'aria-errormessage': string | undefined\n }\n /** The id to give the element that renders the description. */\n descriptionId: string\n /** The id to give the element that renders the error texts. */\n errorId: string\n /**\n * Whether the widget shows the field's errors now: the field is invalid and was edited and\n * left, or the form was submitted.\n */\n isErrorDue: boolean\n /** The texts of the field's errors, as {@link errorsOf} returns them. */\n errors: string[]\n}\n\nfunction isErrorDueFor<TValue>(field: FieldApiOf<TValue>, wasSubmitted: boolean): boolean {\n const { isBlurred, isDirty, isValid } = field.state.meta\n const wasEditedAndLeft = isBlurred && isDirty\n\n return !isValid && (wasEditedAndLeft || wasSubmitted)\n}\n\nfunction idrefsOf(ids: (string | undefined)[]): string | undefined {\n const present = ids.filter((id) => id !== undefined)\n\n return present.length > 0 ? present.join(IDREF_SEPARATOR) : undefined\n}\n\n/**\n * The parts of `field` with the submit state passed in, so the result depends on nothing the\n * caller cannot see.\n */\nexport function fieldPartsFor<TValue>(\n field: FieldApiOf<TValue>,\n options: { description?: string | undefined; wasSubmitted: boolean },\n): FieldParts {\n const id = fieldId(field.form.formId, field.name)\n const descriptionId = `${id}${DESCRIPTION_SUFFIX}`\n const errorId = `${id}${ERROR_SUFFIX}`\n const isErrorDue = isErrorDueFor(field, options.wasSubmitted)\n const errors = errorsOf(field)\n const isErrorNamed = isErrorDue && errors.length > 0\n const hasDescription = options.description !== undefined && options.description !== ''\n\n return {\n control: {\n id,\n name: field.name,\n onBlur: field.handleBlur,\n 'aria-invalid': isErrorDue || undefined,\n 'aria-describedby': idrefsOf([\n hasDescription ? descriptionId : undefined,\n isErrorNamed ? errorId : undefined,\n ]),\n 'aria-errormessage': isErrorNamed ? errorId : undefined,\n },\n descriptionId,\n errorId,\n isErrorDue,\n errors,\n }\n}\n\n/**\n * Wires a widget's control to its field: its id and name, the blur report, and the aria\n * attributes that tie it to its description and its errors.\n *\n * An error is due once the field is invalid and was edited and left, or once the form was\n * submitted. While one is due, `aria-invalid` is `true`; while one with text is due, the control\n * points at the error element through `aria-describedby` and `aria-errormessage`. An id appears in\n * them only when its element is rendered.\n *\n * `fieldParts` reads whether the form was submitted without subscribing to it, so a widget whose\n * field state does not change on a submit keeps its error hidden until it renders again.\n *\n * @deprecated Use {@link useFieldParts}, which subscribes to the submit and renders the widget\n * again.\n * @typeParam TValue - The value type of the field.\n * @param field - The field the widget renders.\n * @param options - What the widget renders besides the control.\n * @param options.description - The description the widget renders below its label, if any. Only\n * whether it is a non-empty string is read.\n * @defaultValue `options.description`: `undefined`, no description is rendered.\n * @returns The props for the control, the ids for the description and error elements, whether an\n * error is due, and the error texts.\n *\n * @example\n * ```tsx\n * const TextField = ({ field, label, description }: FieldOf<string> & { label: string; description?: string }) => {\n * const { control, descriptionId, errorId, isErrorDue, errors } = fieldParts(field, { description })\n *\n * return (\n * <div>\n * <label htmlFor={control.id}>{label}</label>\n * {description && <p id={descriptionId}>{description}</p>}\n * <input {...control} value={field.state.value} onChange={(event) => field.handleChange(event.target.value)} />\n * {isErrorDue && errors.length > 0 && <p id={errorId}>{errors.join(' ')}</p>}\n * </div>\n * )\n * }\n * ```\n */\nexport function fieldParts<TValue>(\n field: FieldApiOf<TValue>,\n options: { description?: string | undefined } = {},\n): FieldParts {\n return fieldPartsFor(field, {\n ...options,\n wasSubmitted: field.form.state.submissionAttempts > 0,\n })\n}\n","import { useSelector } from '@tanstack/react-form'\n\nimport type { FieldApiOf } from './field-of'\nimport { type FieldParts, fieldPartsFor } from './field-parts'\n\n/**\n * Wires a widget's control to its field: its id and name, the blur report, and the aria\n * attributes that tie it to its description and its errors.\n *\n * An error is due once the field is invalid and was edited and left, or once the form was\n * submitted. While one is due, `aria-invalid` is `true`; while one with text is due, the control\n * points at the error element through `aria-describedby` and `aria-errormessage`. An id appears in\n * them only when its element is rendered.\n *\n * The hook subscribes to whether the form was submitted, so a submit renders the widget again even\n * when its field state does not change, as for a field that was left before without an edit.\n *\n * @typeParam TValue - The value type of the field.\n * @param field - The field the widget renders.\n * @param options - What the widget renders besides the control.\n * @param options.description - The description the widget renders below its label, if any. Only\n * whether it is a non-empty string is read.\n * @defaultValue `options.description`: `undefined`, no description is rendered.\n * @returns The props for the control, the ids for the description and error elements, whether an\n * error is due, and the error texts.\n *\n * @example\n * ```tsx\n * const TextField = ({ field, label, description }: FieldOf<string> & { label: string; description?: string }) => {\n * const { control, descriptionId, errorId, isErrorDue, errors } = useFieldParts(field, { description })\n *\n * return (\n * <div>\n * <label htmlFor={control.id}>{label}</label>\n * {description && <p id={descriptionId}>{description}</p>}\n * <input {...control} value={field.state.value} onChange={(event) => field.handleChange(event.target.value)} />\n * {isErrorDue && errors.length > 0 && <p id={errorId}>{errors.join(' ')}</p>}\n * </div>\n * )\n * }\n * ```\n */\nexport function useFieldParts<TValue>(\n field: FieldApiOf<TValue>,\n options: { description?: string | undefined } = {},\n): FieldParts {\n const wasSubmitted = useSelector(field.form.store, (state) => state.submissionAttempts > 0)\n\n return fieldPartsFor(field, { ...options, wasSubmitted })\n}\n"],"mappings":";;;;;AACA,SAAgB,mBAAmB,QAAmD;CACpF,OAAO,OAAO,OAAO,QAAQ,YAAY;AAC3C;;AC8BA,MAAM,iBAAiB,aAAsB,MAAe,UAC1D,OAAO,gBAAgB,aAClB,YAAqC,MAAM,KAAK,IACjD,OAAO,WAAW;AAExB,MAAM,kBAAkB,SAAuB,UAAiC;CAC9E,MAAM,QAAmB,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC;CAEzD,OAAO;EACL,MAAM,MAAM,KAAK,MAAM,UACrB,OAAO,QAAQ,YAAY,aACtB,QAAQ,QAA0C,IAAI,IACvD,KACN;EACA,cAAc,MAAM,KAAK,MAAM,UAAU,cAAc,QAAQ,aAAa,MAAM,KAAK,CAAC;CAC1F;AACF;AAEA,MAAM,cAAsB,OAAyB,WACnD,MAAM,WAAW,OAAO,UAAU,MAAM,OAAO,MAAM,UAAU,SAAS,OAAO,MAAM;AAEvF,MAAM,kBAAkB,OAAqB,WAC3C,WAAW,MAAM,MAAM,OAAO,IAAI,KAAK,WAAW,MAAM,cAAc,OAAO,YAAY;;;;;AAM3F,MAAa,cAAc,EACzB,OACA,SACA,OAAO,OACP,OACA,iBACgC;CAGhC,MAAM,EAAE,MAAM,iBAAiB,YAC7B,MAAM,QACL,UAA8B,eAAe,SAAS,MAAM,KAAK,GAClE,EAAE,SAAS,eAAe,CAC5B;CACA,MAAM,UAAU,QAAQ;CACxB,MAAM,WAAW,OAAO,QAAQ,QAAQ;CACxC,MAAM,YAAkB,MAAM,UAAU,QAAQ,CAAC;CACjD,MAAM,UAAU,UAAwB,MAAM,YAAY,KAAK;CAE/D,IAAI,UAAU,KAAA,GACZ,OACE,qBAAA,YAAA,EAAA,UAAA,CACG,KAAK,KAAK,KAAK,UACd,qBAAC,UAAD,EAAA,UAAA,CACG,WAAW,KAAK,GACjB,oBAAC,UAAD;EAAQ,MAAK;EAAS,uBAAoB;EAAG,eAAe,OAAO,KAAK;EACrE,UAAA,aAAa;CACR,CAAA,CACA,EAAA,GALK,GAKL,CACX,GACD,oBAAC,UAAD;EAAQ,MAAK;EAAS,oBAAiB;EAAG,SAAS;EAChD,UAAA;CACK,CAAA,CACR,EAAA,CAAA;CAIN,OACE,oBAAC,OAAD;EACE,GAAI;EACG;EACD;EACM;EACF;EACI;EACT;EACG;EACR,SAAS,OAAe,SACtB,MAAM,YAAY,OAAO,SAAS,KAAA,IAAY,QAAQ,IAAI,IAAI;EAEhE,OAAO,MAAc,OAAe,MAAM,UAAU,MAAM,EAAE;EAC5D,OAAO,OAAe,WAAmB,MAAM,WAAW,OAAO,MAAM;EACvE,UAAU,OAAe,SAAkB,MAAM,aAAa,OAAO,IAAI;EACzE,aAAa,MAAM,YAAY;CAChC,CAAA;AAEL;;;ACpHA,SAAgB,OACd,KACA,MACoB;CACpB,OAAO,OAAO,OAAO,KAAK,IAAI,IAAI,IAAI,QAAQ,KAAA;AAChD;;;;;;ACFA,SAAgB,UAAU,QAA4B,KAA8B;CAClF,IAAI,OAAO,QAAQ,UACjB,OAAO,GAAG,UAAU,GAAG,GAAG,IAAI;CAGhC,OAAO,WAAW,KAAA,IAAY,MAAM,GAAG,OAAO,GAAG;AACnD;;ACeA,MAAM,SAAS,OAAO,QAAQ;AAC9B,MAAM,UAAU,OAAO,SAAS;AAEhC,MAAM,WAAW,UACf,OAAO,UAAU,YAAY,UAAU,OAAO,QAAQ,KAAA;AAExD,MAAM,WACJ,MACA,OACA,iBAEA,SAAS,KAAA,KACT,KAAK,UAAU,SACf,aAAa,MAAM,OAAO,UAAU,CAAC,OAAO,GAAG,OAAO,KAAK,aAAa,MAAM,CAAC;;;;;;;AAQjF,MAAa,WAAW,EAAE,MAAM,OAAO,eAAwC;CAC7E,MAAM,WAAW,OAA6B,KAAA,CAAS;CACvD,MAAM,EAAE,aAAa,OAAO,cAAc;CAC1C,MAAM,gBAAgB,OAAO,gBAAgB;CAC7C,MAAM,YAAY,OAAO,UAAU;CAEnC,IAAI,EAAE,iBAAiB,YACrB,OAAO,SAAS,QAAQ,KAAK,CAAC;CAGhC,MAAM,cAAc,WAA6B;EAC/C,IAAI,iBAAiB,CAAC,YAAY,MAAM,GACtC,OAAO;EAGT,OAAO,YAAY,SAAS;CAC9B;CAEA,MAAM,UAAU,EAAE,aAA2C;EAC3D,IAAI,CAAC,MAAM,QAAQ,SAAS,GAC1B,OAAO,WAAW,MAAM;EAG1B,MAAM,eAAe,UAAU,KAAK,SAAiB,MAAM,QAAQ,IAAI,CAAC;EACxE,IAAI,QAAQ,SAAS,SAAS,OAAO,YAAY,GAC/C,SAAS,UAAU;GAAE;GAAO;GAAc,UAAU,WAAW,MAAM;EAAE;EAGzE,OAAO,SAAS,SAAS;CAC3B;CAEA,OACE,oBAAC,KAAK,WAAN;EAAgB,UAAU;EACtB,WAAA,aAAa;GACb,IAAI,aAAa,QACf,OAAO;GAGT,OAAO,SAAS,QAAQ,YAAY,MAAM,QAAQ,IAAI,KAAK,CAAC;EAC9D;CACc,CAAA;AAEpB;;ACxFA,MAAM,aAAa;AACnB,MAAM,WAAW;;AAGjB,SAAgB,YAAY,MAAsB;CAChD,OAAO,KAAK,WAAW,YAAY,QAAQ;AAC7C;;ACJA,MAAa,oCAAyC,IAAI,IAAI;;AAG9D,MAAa,gBAA8C,cAAc,iBAAiB;ACiB1F,MAAM,oCAAyC,IAAI,IAAI;CAjBrD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AAMqD,CAAc;;AAGrE,SAAgB,WAAW,OAGzB;CACA,MAAM,UAAU,OAAO,QAAQ,KAAK;CAGpC,OAAO;EACL,KAAK,OAAO,YAAY,QAAQ,QAAQ,CAAC,SAAS,kBAAkB,IAAI,GAAG,CAAC,CAAC;EAC7E,cAAc,OAAO,YAAY,QAAQ,QAAQ,CAAC,SAAS,CAAC,kBAAkB,IAAI,GAAG,CAAC,CAAC;CACzF;AACF;;AC/BA,MAAM,+BAAe,IAAI,QAAgB;AAEzC,MAAM,qBACJ,OAAO,YAAY,eAAe,QAAQ,IAAI,aAAa;;AAG7D,SAAgB,SAAS,OAAe,SAAuB;CAC7D,IAAI,aAAa,KAAK,aAAa,IAAI,KAAK,GAC1C;CAGF,aAAa,IAAI,KAAK;CACtB,QAAQ,KAAK,OAAO;AACtB;;AC8DA,MAAM,aAAa;AAEnB,MAAM,gBAAgB;AAEtB,MAAM,sBAAiC;AAEvC,MAAM,eAAe,SACnB,SAAS,KAAA,IAAY,aAAa,IAAI,KAAK;AAE7C,MAAM,UAAU,UACd,OAAO,UAAU,YAAY,UAAU,QAAQ,YAAY;AAI7D,MAAM,cAAc,SAA0B;CAC5C,IAAI,OAAO,IAAI,GACb,OAAO;CAET,MAAM,EAAE,QAAQ,WAAW,IAAI;CAC/B,MAAM,SACJ,OAAO,IAAI,WAAW,YAAY,IAAI,WAAW,OAAO,OAAO,OAAO,IAAI,MAAM,IAAI,CAAC;CAEvF,OAAO,IAAI,WAAW,KAAA,KAAa,OAAO,WAAW,KAAK,OAAO,OAAO,EAAE;AAC5E;AAEA,MAAM,QAAQ,EAAE,OAAO,MAAM,OAAO,MAAM,OAAO,YAAkC;CACjF,MAAM,EAAE,QAAQ,WAAW,KAAK;CAChC,MAAM,OAAO,IAAI;CACjB,MAAM,gBAAgB,UAAU,KAAA,KAAa,WAAW,IAAI;CAC5D,IAAI,UAAU,KAAA,KAAa,CAAC,eAC1B,SAAS,OAAO,gCAAgC,KAAK,IAAI,cAAc,oBAAoB;CAG7F,OACE,oBAAC,OAAD;EACS;EACD;EACN,OAAO;EACP,MAAM,UAAU,MAAM,KAAK;EAC3B,OAAO,gBAAgB,QAAQ,KAAA;CAChC,CAAA;AAEL;AAEA,MAAM,gBAAgB,EAAE,OAAO,MAAM,OAAO,MAAM,OAAO,YAA0C;CACjG,MAAM,EAAE,KAAK,iBAAiB;CAC9B,MAAM,gBAAgB,IAAI,aAAa;CAEvC,IAAI,OAAO,IAAI,WAAW,UAAU;EAClC,MAAM,SAAS,OAAO,MAAM,SAAS,IAAI,MAAM;EAC/C,IAAI,WAAW,KAAA,GACb,MAAM,IAAI,MAAM,mBAAmB,KAAK,mCAAmC,IAAI,OAAO,GAAG;EAI3F,MAAM,QAAQ,mBAAmB,MAAM,IACnC,EAAE,UAAU,cAAc,IAAI,YAAY,IAAI,CAAC,EAAE,IACjD,KAAA;EAEJ,OACE,oBAAC,KAAK,UAAN;GAAe,GAAI;GAAc,MAAM;GACnC,WAAA,UAAU,oBAAC,QAAD;IAAQ,GAAI;IAAO,GAAI;IAAO,GAAI;IAAc;GAAQ,CAAA;EACvD,CAAA;CAEnB;CAEA,IAAI,OAAO,IAAI,WAAW,YACxB,OACE,oBAAC,KAAK,UAAN;EAAe,GAAI;EAAc,MAAM;EACpC,UAAA,IAAI;CACQ,CAAA;CAInB,IAAI,OAAO,IAAI,SAAS,YAAY,IAAI,SAAS,MAC/C,OACE,oBAAC,KAAK,UAAN;EAAe,GAAI;EAAc,MAAM;EAAM,MAAM;EAC/C,WAAA,UACA,oBAAC,YAAD;GACS;GACP,SAAS;GACT,OAAO,MAAM,UAAU;GAChB;GACP,aAAa,OAAO,cAClB,oBAAC,MAAD;IACS;IACD;IACN,OAAO,MAAM;IACP;IACC;IACP,OAAO;GACR,CAAA;EAEJ,CAAA;CAEU,CAAA;CAInB,IAAI,OAAO,IAAI,WAAW,YAAY,IAAI,WAAW,MACnD,OAAO;CAGT,OACE,qBAAA,YAAA,EAAA,UAAA,CACG,OAAO,KAAK,YAAY,CAAC,CAAC,SAAS,KAClC,oBAAC,KAAK,UAAN;EAAe,GAAI;EAAc,MAAM;EACpC,UAAA;CACY,CAAA,GAEjB,oBAAC,cAAD;EACS;EACD;EACN,QAAQ,IAAI;EACZ,QAAQ,MAAM,QAAQ,IAAI,MAAM,IAAI,IAAI,SAAS,KAAA;EAC3C;EACC;CACR,CAAA,CACD,EAAA,CAAA;AAEN;AAEA,MAAM,SAAS,EAAE,OAAO,MAAM,OAAO,MAAM,YAAmC;CAC5E,MAAM,QAAQ;EAAE,GAAG,WAAW,KAAK;EAAG,QAAQ;CAAM;CAEpD,OACE,oBAAC,SAAD;EAAe;EAAM,OAAO,MAAM;EAC9B,WAAA,UACA,oBAAC,cAAD;GACS;GACD;GACN,OAAO;GACD;GACC;GACA;EACR,CAAA;CAEI,CAAA;AAEb;AAEA,MAAM,aAAa,EAAE,OAAO,WAAsD;CAChF,MAAM,QAAQ,OAAO,MAAM,QAAQ,IAAI;CACvC,IAAI,UAAU,KAAA,GACZ,MAAM,IAAI,MACR,0BAA0B,YAAY,MAAM,IAAI,EAAE,UAAU,KAAK,sCACnE;CAGF,OACE,oBAAC,OAAD;EACE,OAAO,MAAM;EACb,MAAM,MAAM;EACL;EACP,MAAM,UAAU,MAAM,MAAM,IAAI;EAChC,OAAO,MAAM;CACd,CAAA;AAEL;AAEA,MAAM,eAAe,EAAE,OAAO,eAC3B,MAAM,QAAQ,OAAO,IAAK,UAAgC,CAAC,EAAA,CAAG,KAAK,OAAO,UAEzE,oBAAC,YAAD;CAA+B;CAAc;AAAQ,GAApC,KAAoC,CACtD;AAEH,MAAM,eAAe,EAAE,OAAO,OAAO,YAAwC;CAC3E,MAAM,WAAW,oBAAC,aAAD;EAAoB;EAAO,SAAS,MAAM;CAAS,CAAA;CACpE,MAAM,UAAU,MAAM,MAAM,UAAU;CAEtC,IAAI,YAAY,KAAA,GACd,OACE,oBAAC,SAAD;EAAS,GAAI;EAAO,OAAO,MAAM;EAC9B;CACM,CAAA;CAIb,OACE,qBAAC,YAAD;EAAU,GAAI;EAAd,UAAA,CACE,oBAAC,UAAD,EAAA,UAAS,MAAM,QAA0B,CAAA,GACxC,QACO;;AAEd;AAEA,MAAM,WAAW,EAAE,OAAO,OAAO,YAAwC;CACvE,MAAM,WAAW,oBAAC,aAAD;EAAoB;EAAO,SAAS,MAAM;CAAM,CAAA;CACjE,MAAM,MAAM,MAAM,MAAM,UAAU;CAElC,IAAI,QAAQ,KAAA,GACV,OAAO,oBAAC,KAAD;EAAK,GAAI;EAAQ;CAAc,CAAA;CAGxC,OACE,oBAAC,OAAD;EAAK,GAAI;EAAO,oBAAiB;EAC9B;CACE,CAAA;AAET;AAEA,MAAM,iBAAiB,EAAE,OAAO,OAAO,YAAwC;CAC7E,MAAM,OAAO,MAAM;CACnB,MAAM,YAAY,OAAO,MAAM,MAAM,gBAAgB,IAAI;CACzD,IAAI,cAAc,KAAA,GAChB,MAAM,IAAI,MACR,0BAA0B,YAAY,MAAM,IAAI,EAAE,2CAA2C,KAAK,GACpG;CAGF,OAAO,oBAAC,WAAD,EAAW,GAAI,MAAQ,CAAA;AAChC;AAEA,MAAM,cAAc,EAAE,OAAO,OAAO,YAAwC;CAC1E,IAAI,aAAa,OACf,OAAO,oBAAC,aAAD;EAAoB;EAAc;EAAc;CAAQ,CAAA;CAEjE,IAAI,SAAS,OACX,OAAO,oBAAC,SAAD;EAAgB;EAAc;EAAc;CAAQ,CAAA;CAE7D,IAAI,OAAO,MAAM,cAAc,UAC7B,OAAO,oBAAC,eAAD;EAAsB;EAAc;EAAc;CAAQ,CAAA;CAEnE,IAAI,OAAO,MAAM,WAAW,YAC1B,OAAQ,MAAM,OAA2C,MAAM,IAAI;CAGrE,OAAO;AACT;AAEA,MAAM,cAAc,EAAE,OAAO,YAAwC;CACnE,IAAI,OAAO,UAAU,UACnB,OAAO,oBAAC,WAAD;EAAkB;EAAO,MAAM;CAAQ,CAAA;CAGhD,OACE,oBAAC,SAAD;EAAS,MAAM,MAAM;EAAa;EAC9B,WAAA,UAAU,oBAAC,YAAD;GAAmB;GAAc;GAAc;EAAQ,CAAA;CAC5D,CAAA;AAEb;AAEA,MAAM,gBAAgB,EAAE,QAAQ,GAAG,YAAmC;CACpE,IAAI,WAAW,KAAA,GACb,OAAO,oBAAC,aAAD;EAAoB;EAAO,SAAS;CAAS,CAAA;CAGtD,OAAO,OAAO,KAAK,MAAM,MAAM,CAAC,CAAC,KAAK,SAAS,oBAAC,WAAD;EAA6B;EAAa;CAAO,GAAjC,IAAiC,CAAC;AACnG;;;;;;;;;;AAWA,MAAa,UAAU,EAAE,OAAO,MAAM,QAAQ,QAAQ,oBACpD,oBAAC,eAAD;CAAe,OAAO,iBAAiB;CACrC,UAAA,oBAAC,cAAD;EAAqB;EAAa;EAAc;EAAgB;EAAQ,MAAM,KAAA;CAAY,CAAA;AAC7E,CAAA;;ACtTjB,MAAM,OAA4B,EAAE,uBAAO,IAAI,IAAI,EAAE;AAErD,MAAM,eACJ;AACF,MAAM,mBACJ;AACF,MAAM,kBACJ;AAEF,MAAM,aAAa;AAEnB,MAAM,oBAAoB,WACxB,OAAO,WAAW,YAAY,WAAW,QAAQ,eAAe;AAElE,MAAM,cAAc,QAAqB,MAA0B,UACjE,OAAO,YACL,OAAO,QAAQ,MAAM,CAAC,CAAC,KAAK,CAAC,KAAK,WAAW,CAC3C,KACA,WAAW,OAAO,UAAU,MAAM,GAAG,GAAG,KAAK,CAC/C,CAAC,CACH;AAEF,SAAS,WAAW,OAAe,MAAc,OAA6B;CAC5E,MAAM,EAAE,QAAQ,WAAW,KAAK;CAEhC,IAAI,OAAO,IAAI,WAAW,UAAU;EAClC,MAAM,SAAS,OAAO,MAAM,SAAS,IAAI,MAAM;EAC/C,IAAI,WAAW,KAAA,KAAa,CAAC,mBAAmB,MAAM,GACpD;EAEF,MAAM,SAAS,IAAI,YAAY,IAAI,CAAC;EAEpC,OAAO,OAAO;CAChB;CACA,IAAI,OAAO,IAAI,SAAS,YAAY,IAAI,SAAS,MAC/C,OAAO,CAAC,WAAW,IAAI,MAAM,UAAU,MAAM,UAAU,GAAG,KAAK,CAAC;CAElE,IAAI,OAAO,IAAI,WAAW,YAAY,IAAI,WAAW,MACnD,OAAO,WAAW,IAAI,QAAuB,MAAM,KAAK;AAI5D;AAEA,MAAM,aAAa,UACjB,aACG,MAAM,QAAQ,CAAC,EAAA,CAAG,QAA4B,QAAQ,YAAY;CACjE,MAAM,MAAM,OAAO,YAAY,WAAW,QAAQ,MAAM;CAExD,OAAO,UAAU,QAAQ,OAAO,QAAQ,WAAW,MAAM,OAAO,GAAG,CAAC;AACtE,GAAG,KAAA,CAAS,KAAK,EACnB;AAEF,MAAM,YAAY,SAChB,KAAK,MAAM,GAAG,KAAK,IAAI,KAAK,YAAY,GAAG,GAAG,KAAK,YAAY,GAAG,CAAC,CAAC;AAGtE,MAAM,UAAU,MAAc,WAAoD;CAChF,KAAK,IAAI,YAAY,MAAM,cAAc,IAAI,YAAY,SAAS,SAAS,GACzE,IAAI,OAAO,IAAI,SAAS,GACtB,OAAO;AAKb;AAGA,MAAM,wBAAmC,KAAA;AAEzC,MAAM,aAAa,UACjB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;;;;;;;AAQvD,SAAgB,kBAAkB,EAChC,QACA,gBACA,QACA,WAC0C;CAC1C,IAAI,CAAC,iBAAiB,MAAM,GAC1B,OAAO;CAET,IAAI,gBACF,OAAO,EAAE,SAAS,aAAa;CAGjC,MAAM,QAAqB;EAAE;EAAS,0BAAU,IAAI,IAAI;CAAE;CAC1D,MAAM,SAAS,WAAW,QAAQ,KAAA,GAAW,KAAK;CAClD,IAAI;CACJ,IAAI;EACF,SAAS,OAAO,YAAY,CAAC,SAAS,MAAM;CAC9C,SAAS,OAAO;EACd,OAAO,EAAE,SAAS,GAAG,gBAAgB,IAAI,UAAU,KAAK,IAAI;CAC9D;CACA,IAAI,kBAAkB,SAAS;EAC7B,OAAO,KAAK,KAAA,GAAW,eAAe;EAEtC,OAAO,EAAE,SAAS,iBAAiB;CACrC;CAEA,MAAM,UAAU,OAAO,UAAU,CAAC,EAAA,CAAG,KAAK,UAAU,OAAO,UAAU,KAAK,GAAG,MAAM,QAAQ,CAAC;CAE5F,OAAO,EAAE,OAAO,IAAI,IAAI,OAAO,QAAQ,SAAS,SAAS,KAAA,CAAS,CAAC,EAAE;AACvE;;ACnIA,MAAM,eAAe,EAAE,OAAO,WAA0C;CACtE,IAAI,MAAM,SAAS,UAAU,MAAM,SAAS,UAC1C,OAAO;CAGT,OAAO,MAAM,SAAS,YAAY,CAAC,KAAK,MAAM;AAChD;;;;;AAMA,MAAa,kBAAqC,UAAU;CAC1D,IAAI,WAAgC,CAAC;CACrC,uBAAuB;EACrB,GAAG;EACH,gBAAgB,EAAE,iBAAiB;GACjC,WAAW;EACb;CACF,CAAC;CAED,MAAM,SAAS,MAAM,MAAM,QAAQ,MAAM,YAAY,iBAAiB,MAAM,YAAY;CACxF,IAAI,WAAW,KAAA,KAAa,CAAC,YAAY,KAAK,GAC5C,OAAO,MAAM,cAAc;EAAE,YAAY;EAAU,MAAM,MAAM;CAAK,CAAC;CAGvE,OAAO,MAAM,cAAc;EACzB,YAAY,CAAC,GAAG,UAAU;GAAE,IAAI;GAAQ,OAAO;EAAU,CAAC;EAC1D,MAAM,MAAM;CACd,CAAC;AACH;;ACtBA,MAAM,eAAqB;AAE3B,MAAM,aAAa,SACjB,SAAS,gBAAgB,SAAS;AAEpC,MAAM,YAAY,SAChB,SAAS,eAAe,gBAAgB,IAAI;AAE9C,MAAM,WAAW,MAAY,YAA4C;CACvE,MAAM,OAAa,UAAU,IAAI,IAAI,cAAc;CAEnD,OAAO,UAAU,GAAG,KAAK,SAAS;AACpC;AAEA,MAAM,YAAY,UAChB,OAAO,UAAU,YAAY,UAAU,OAAQ,QAAoB,CAAC;AAGtE,MAAM,gBAAgB,UACpB,OAAO,YAAY,OAAO,QAAQ,SAAS,KAAK,CAAC,CAAC,CAAC,QAAQ,GAAG,WAAW,UAAU,KAAA,CAAS,CAAC;;;;;;;;AAS/F,SAAgB,eAAe,EAAE,QAAQ,WAAsD;CAC7F,MAAM,EACJ,QACA,YACA,QAAQ,mBACR,QAAQ,mBACR,GAAG,kBACD;CACJ,MAAM,EAAE,OAAO,cAAc,GAAG,WAAW,SAAS,UAAU;CAC9D,MAAM,UAAU,OAAO,UAAU;CACjC,MAAM,OAAO,QAAQ,MAAM,OAAO;CAClC,MAAM,kBAAkB,aAAa,OAAO;CAC5C,MAAM,aAAa;EACjB,GAAG,SAAS,cAAc,UAAU;EACpC,GAAG,aAAa,gBAAgB,UAAU;CAC5C;CAEA,OAAO;EACL,GAAI,UAAU,IAAI,KAAK,EAAE,iBAAiB,SAAS,IAAI,EAAE;EACzD,GAAI,WACF,OAAO,oBAAoB,KAAA,KAAa,EAAE,iBAAiB,OAAO,gBAAgB;EACpF,GAAG;EACH,GAAG;EACH,YAAY,WAAW,UAAU,KAAA,IAAY;GAAE,GAAG;IAAa,OAAO;EAAO,IAAI;EACjF,WAAW;GAAE,GAAG,SAAS,cAAc,SAAS;GAAG,GAAG,aAAa,gBAAgB,SAAS;EAAE;CAChG;AACF;;AC7BA,MAAM,yBACJ;AAEF,MAAM,kBAAkB,EACtB,SACA,QACA,eACA,SACA,GAAG,gBACiC;CACpC,MAAM,cAAc,eAAe;EAAE;EAAQ;CAAQ,CAAC;CACtD,IAAI,YAAY,kBAAkB,KAAA,GAChC,MAAM,IAAI,MAAM,sBAAsB;CAGxC,MAAM,OAAO,QAAQ,WAAW,WAAW;CAC3C,MAAM,OAAO,QAAQ,MAAM,UAAU;CACrC,MAAM,UACJ,oBAAC,QAAD;EACE,OAAO,QAAQ;EACT;EACN,QAAQ,OAAO;EACf,QAAQ,OAAO;EACA;CAChB,CAAA;CAEH,MAAM,gBAAgB,UAA4C;EAChE,MAAM,eAAe;EACrB,MAAM,gBAAgB;EACtB,KAAU,aAAa;CACzB;CAEA,OACE,oBAAC,KAAK,SAAN,EAAA,UACE,oBAAC,QAAD;EAAM,YAAA;EAAW,GAAI;EAAW,UAAU;EACvC,UAAA,SAAS,KAAA,IAAY,UAAU,oBAAC,MAAD,EAAA,UAAO,QAAc,CAAA;CACjD,CAAA,EACM,CAAA;AAElB;AAEA,MAAM,qBAAqB,eACzB,OAAO,eAAe,YAAY,eAAe,QAAQ,WAAW,aAChE,WAAW,UAAU,OACrB;AAEN,MAAM,mBAAmB,SAAsB,WAA6C;CAC1F,MAAM,SAAS,kBAAkB;EAC/B,QAAQ,OAAO;EACf,gBAAgB,kBAAkB,OAAO,UAAU;EACnD,QAAS,OAAO,UAAU,CAAC;EAC3B,SAAS,QAAQ,MAAM;CACzB,CAAC;CACD,IAAI,aAAa,QAAQ;EACvB,SAAS,QAAQ,OAAO,OAAO;EAE/B,uBAAO,IAAI,IAAI;CACjB;CAEA,OAAO,OAAO;AAChB;;;;;;AAOA,SAAgB,WACd,SACA,QAC0C;CAC1C,IAAI;CAEJ,QAAQ,UAAyC;EAC/C,kBAAkB,gBAAgB,SAAS,MAAM;EAEjD,OACE,oBAAC,gBAAD;GAAgB,GAAI;GAAgB;GAAiB;GAAuB;EAAgB,CAAA;CAEhG;AACF;;AC/FA,SAAS,cAKP,OACiD;CACjD,MAAM,EAAE,YAAY,2BAA2B,GAAG,aAAa,eAAe;EAC5E,cAAc,MAAM;EACpB,aAAa,MAAM;EACnB,iBAAiB,MAAM;EACvB,gBAAgB,MAAM;CACxB,CAAC;CAED,SAAS,eAIP,WAQA;EACA,MAAM,eAA2B,UAAU,WAAW,CAAC;EACvD,MAAM,iBAA4B,UAAU,aAAa,CAAC;EAG1D,OAAO,cAAc;GACnB,cAAc,MAAM;GACpB,aAAa,MAAM;GACnB,SAAS;IAAE,GAAG,MAAM;IAAS,GAAG;GAAa;GAC7C,gBAAgB;IAAE,GAAG,MAAM;IAAgB,GAAG,UAAU;GAAe;GAEvE,WAAW;IAAE,GAAG,MAAM;IAAW,GAAG;GAAe;EACrD,CAAC;CACH;CAIA,MAAM,UAAuB;EAC3B,YAAY,SAAS;EACrB,OAAO;GACL,SAAS,MAAM;GACf,gBAAgB,MAAM;GACtB,WAAW,MAAM;EACnB;CACF;CAGA,MAAM,eAAe,UACnB,cAAc,QAAQ;EAAE,GAAG;EAAO,OAAO,QAAQ;CAAM,CAAC;CAE1D,OAAO;EACL;EACA,aAAa,WAAW,WAAW,SAAS,MAAM;EAElD,QAAQ;EAEQ;CAKlB;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCA,SAAgB,eAKd,QAKiD;CAEjD,OAAO,cAAqD;EAC1D,cAAc,OAAO;EACrB,aAAa,OAAO;EACpB,SAAS,OAAO;EAChB,gBAAgB,OAAO,kBAAmB,CAAC;EAC3C,WAAW,OAAO,aAAc,CAAC;CACnC,CAAC;AACH;;ACtJA,SAAS,OAAO,OAAoC;CAClD,IAAI,OAAO,UAAU,UACnB,OAAO;CAET,IAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,EAAE,aAAa,QAChE;CAGF,OAAO,OAAO,MAAM,YAAY,WAAW,MAAM,UAAU,KAAA;AAC7D;AAEA,SAAS,QAAQ,MAA0C;CACzD,OAAO,SAAS,KAAA,KAAa,SAAS;AACxC;;;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,SAAiB,OAAqC;CACpE,MAAM,QAAQ,MAAM,MAAM,KAAK,OAAO,KAAK,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,OAAO,OAAO;CAEvE,OAAO,CAAC,GAAG,IAAI,IAAI,KAAK,CAAC;AAC3B;;AC3CA,MAAM,kBAAkB;AACxB,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;AAuBhC,SAAgB,QAAQ,QAAgB,MAAsB;CAC5D,OAAO,GAAG,OAAO,GAAG,KAAK,QAAQ,iBAAiB,uBAAuB;AAC3E;;ACtBA,MAAM,qBAAqB;AAC3B,MAAM,eAAe;AACrB,MAAM,kBAAkB;AAmCxB,SAAS,cAAsB,OAA2B,cAAgC;CACxF,MAAM,EAAE,WAAW,SAAS,YAAY,MAAM,MAAM;CAGpD,OAAO,CAAC,YAFiB,aAAa,WAEE;AAC1C;AAEA,SAAS,SAAS,KAAiD;CACjE,MAAM,UAAU,IAAI,QAAQ,OAAO,OAAO,KAAA,CAAS;CAEnD,OAAO,QAAQ,SAAS,IAAI,QAAQ,KAAK,eAAe,IAAI,KAAA;AAC9D;;;;;AAMA,SAAgB,cACd,OACA,SACY;CACZ,MAAM,KAAK,QAAQ,MAAM,KAAK,QAAQ,MAAM,IAAI;CAChD,MAAM,gBAAgB,GAAG,KAAK;CAC9B,MAAM,UAAU,GAAG,KAAK;CACxB,MAAM,aAAa,cAAc,OAAO,QAAQ,YAAY;CAC5D,MAAM,SAAS,SAAS,KAAK;CAC7B,MAAM,eAAe,cAAc,OAAO,SAAS;CACnD,MAAM,iBAAiB,QAAQ,gBAAgB,KAAA,KAAa,QAAQ,gBAAgB;CAEpF,OAAO;EACL,SAAS;GACP;GACA,MAAM,MAAM;GACZ,QAAQ,MAAM;GACd,gBAAgB,cAAc,KAAA;GAC9B,oBAAoB,SAAS,CAC3B,iBAAiB,gBAAgB,KAAA,GACjC,eAAe,UAAU,KAAA,CAC3B,CAAC;GACD,qBAAqB,eAAe,UAAU,KAAA;EAChD;EACA;EACA;EACA;EACA;CACF;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyCA,SAAgB,WACd,OACA,UAAgD,CAAC,GACrC;CACZ,OAAO,cAAc,OAAO;EAC1B,GAAG;EACH,cAAc,MAAM,KAAK,MAAM,qBAAqB;CACtD,CAAC;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC9FA,SAAgB,cACd,OACA,UAAgD,CAAC,GACrC;CACZ,MAAM,eAAe,YAAY,MAAM,KAAK,QAAQ,UAAU,MAAM,qBAAqB,CAAC;CAE1F,OAAO,cAAc,OAAO;EAAE,GAAG;EAAS;CAAa,CAAC;AAC1D"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@formdef/core",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Describe a form once as a typed object, render it as a React component, with the compiler checking that every widget fits its value. Built on TanStack Form.",
5
5
  "keywords": [
6
6
  "react",