@samline/forms 2.7.1 → 2.8.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.
Files changed (52) hide show
  1. package/CHANGELOG.md +302 -0
  2. package/README.md +110 -122
  3. package/dist/browser/global.d.ts +1 -13
  4. package/dist/browser/global.global.js +12 -6179
  5. package/dist/browser/global.global.js.map +1 -1
  6. package/dist/browser/index.cjs +125 -56
  7. package/dist/browser/index.cjs.map +1 -1
  8. package/dist/browser/index.js +1 -1
  9. package/dist/{chunk-MMHX34WB.js → chunk-23VZAYA3.js} +126 -57
  10. package/dist/chunk-23VZAYA3.js.map +1 -0
  11. package/dist/index.cjs +125 -56
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.js +1 -1
  14. package/package.json +35 -7
  15. package/dist/chunk-MMHX34WB.js.map +0 -1
  16. package/docs/api/add-cleanup.md +0 -52
  17. package/docs/api/append.md +0 -96
  18. package/docs/api/auto-submit.md +0 -91
  19. package/docs/api/clear-errors.md +0 -69
  20. package/docs/api/destroy.md +0 -80
  21. package/docs/api/disable-auto-submit.md +0 -65
  22. package/docs/api/element.md +0 -78
  23. package/docs/api/form.md +0 -156
  24. package/docs/api/format.md +0 -270
  25. package/docs/api/get-data.md +0 -90
  26. package/docs/api/get-field.md +0 -87
  27. package/docs/api/get-state.md +0 -111
  28. package/docs/api/get-value.md +0 -114
  29. package/docs/api/index.md +0 -55
  30. package/docs/api/observe.md +0 -126
  31. package/docs/api/on-submit.md +0 -94
  32. package/docs/api/parse-form-data.md +0 -94
  33. package/docs/api/prefill.md +0 -89
  34. package/docs/api/regex.md +0 -155
  35. package/docs/api/reset.md +0 -61
  36. package/docs/api/revalidate.md +0 -52
  37. package/docs/api/set-errors.md +0 -97
  38. package/docs/api/set-value.md +0 -120
  39. package/docs/api/subscribe.md +0 -107
  40. package/docs/api/unwatch.md +0 -74
  41. package/docs/api/validate-field-value.md +0 -144
  42. package/docs/api/validate-values.md +0 -103
  43. package/docs/api/validate.md +0 -102
  44. package/docs/api/watch.md +0 -117
  45. package/docs/browser.md +0 -156
  46. package/docs/css-styling.md +0 -191
  47. package/docs/getting-started.md +0 -222
  48. package/docs/options.md +0 -326
  49. package/docs/recipes.md +0 -531
  50. package/docs/typescript.md +0 -615
  51. package/docs/vanilla.md +0 -12
  52. /package/{LICENSE → LICENSE.md} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/browser/index.ts","../../src/api/append.ts","../../src/api/add-cleanup.ts","../../src/api/auto-submit.ts","../../src/api/clear-errors.ts","../../src/core/format-helpers.ts","../../src/core/formatter-loader.ts","../../src/api/format.ts","../../src/api/destroy.ts","../../src/api/disable-auto-submit.ts","../../src/core/serialize.ts","../../src/api/get-data.ts","../../src/api/get-field.ts","../../src/core/state.ts","../../src/api/get-state.ts","../../src/core/dom.ts","../../src/api/get-value.ts","../../src/api/observe.ts","../../src/api/on-submit.ts","../../src/api/prefill.ts","../../src/api/reset.ts","../../src/api/validate.ts","../../src/api/revalidate.ts","../../src/api/set-errors.ts","../../src/api/set-value.ts","../../src/api/subscribe.ts","../../src/api/unwatch.ts","../../src/api/watch.ts","../../src/core/validation.ts","../../src/core/controller.ts","../../src/api/form.ts","../../src/browser/registry.ts","../../src/browser/global.ts"],"sourcesContent":["export {\n available,\n default,\n destroyForm,\n form,\n newForm\n} from './global'\nexport type { FormsApi, FormsAvailable, NewFormInput } from './registry'\n","// api/append.ts\n// Appends DOM content to the bound form. If a class is provided, removes\n// any existing node with that class first to avoid duplicates.\n// Returns the created node or null when no element is bound.\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\nimport type { AppendContentOptions } from '../core/types'\n\nexport const createAppend =\n (state: FormControllerState, helpers: FormControllerHelpers) =>\n ({ tag, content, class: className, atStart = false }: AppendContentOptions) => {\n if (!state.element) return null\n\n helpers.clearFieldCache()\n\n if (className) {\n const classSelector = className.trim().split(/\\s+/)[0]\n const existing = classSelector\n ? state.element.getElementsByClassName(classSelector)[0]\n : null\n existing?.remove()\n }\n\n const node = document.createElement(tag)\n if (className) node.className = className\n node.innerHTML = content\n\n if (atStart && state.element.firstChild) {\n state.element.insertBefore(node, state.element.firstChild)\n } else {\n state.element.appendChild(node)\n }\n\n return node\n }\n","// api/add-cleanup.ts\n// Registers controller-owned teardown work and returns an unregister function.\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\nimport type { FormCleanup } from '../core/types'\n\nexport const createAddCleanup =\n (state: FormControllerState, _helpers: FormControllerHelpers) =>\n (cleanup: FormCleanup): (() => void) => {\n if (state.isDestroyed) {\n cleanup()\n return () => undefined\n }\n\n const registration = () => cleanup()\n state.cleanups.add(registration)\n let active = true\n return () => {\n if (!active) return\n active = false\n state.cleanups.delete(registration)\n }\n }\n","// api/auto-submit.ts\n// Enables native auto-submit. Pass `true` to enable, `false` to disable,\n// or an { debounce } object to delay.\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\n\nexport const createAutoSubmit =\n (state: FormControllerState, helpers: FormControllerHelpers) =>\n (next: boolean | { debounce?: number } = true) => {\n state.autoSubmitEnabled = next !== false\n if (!state.autoSubmitEnabled && state.autoSubmitTimer) {\n clearTimeout(state.autoSubmitTimer)\n state.autoSubmitTimer = null\n }\n state.autoSubmitDebounce =\n typeof next === 'object' && next ? next.debounce ?? 0 : 0\n helpers.notifySubscribers()\n return state.api!\n }\n","// api/clear-errors.ts\n// Clears manual errors. With no args, clears every manual error. With a\n// list, clears only those fields.\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\n\nexport const createClearErrors =\n (state: FormControllerState, helpers: FormControllerHelpers) =>\n (fields?: string[]) => {\n if (!fields) {\n state.manualErrors = {}\n helpers.syncVisualState()\n helpers.notifySubscribers()\n return state.api!\n }\n\n for (const field of fields) {\n delete state.manualErrors[field]\n }\n helpers.syncVisualState(fields)\n helpers.notifySubscribers()\n return state.api!\n }\n","// Low-level helpers used by `api/format.ts`:\n// - DOM cursor tracking equivalent to cleave.js' `getNextCursorPosition`\n// (so typing/backspacing inside formatted inputs keeps the caret\n// in a sensible place, even when delimiters are inserted or removed).\n// - `postDelimiterBackspace` contract: after a deletion, the caret\n// is pulled backwards past any leading delimiter so the user never\n// ends up parked with a format character on the caret's left.\n// - Hidden-mirror lookup & creation: each formatted input gets a\n// hidden sibling (`type=\"hidden\"`) that holds the backend-ready\n// `raw` value under the **canonical** field name (the one the\n// developer wrote in the HTML and the one the backend reads).\n// The visible input is renamed to `<field>_displayed` on the\n// first `format()` call. Both names are first-class in the\n// controller's API — see `docs/api/format.md` for the contract.\n//\n// The helpers here are pure DOM utilities — they do not know about the\n// controller state.\n\nimport type { FormFieldElement } from './types'\n\n/**\n * Marks a hidden input as the raw mirror of a formatted field.\n * Presence of this attribute is how `format()` distinguishes\n * mirrors it owns (created at `format()` time) from mirrors the\n * developer pre-authored in the HTML (which survive `destroy()`).\n */\nexport const FORMATTER_RAW_ATTRIBUTE = 'data-formatter-raw-for'\n\n// Resolve a field by name. Returns the first matching element or `null`.\nexport const getFormatterField = (\n form: HTMLFormElement,\n name: string\n): HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | null => {\n const selector = `[name=\"${cssEscape(name)}\"]`\n const node = form.querySelector(selector)\n if (\n node instanceof HTMLInputElement ||\n node instanceof HTMLTextAreaElement ||\n node instanceof HTMLSelectElement\n ) {\n return node\n }\n return null\n}\n\n// Minimal CSS.escape polyfill for selectors that may contain special\n// characters (`[1].phone`, `email[primary]`, etc.).\nconst cssEscape = (value: string): string => {\n if (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') {\n return CSS.escape(value)\n }\n return value.replace(/([!\"#$%&'()*+,./:;<=>?@\\[\\\\\\]^`{|}~])/g, '\\\\$1')\n}\n\n// Find the visible (non-hidden) input that owns the formatted pair for\n// `fieldName`. Looks up by the display name (defaults to\n// `<fieldName>_displayed`, or whatever the developer passed in\n// `FieldFormatConfig.displayField`). Returns `null` when no visible\n// field matches.\nexport const findVisibleField = (\n form: HTMLFormElement,\n displayName: string\n): HTMLInputElement | HTMLTextAreaElement | null => {\n const node = form.querySelector(\n `[name=\"${cssEscape(displayName)}\"]:not([type=\"hidden\"])`\n )\n if (node instanceof HTMLInputElement || node instanceof HTMLTextAreaElement) {\n return node\n }\n return null\n}\n\n// Find the hidden input that mirrors `raw` for the given canonical\n// field name. Returns the existing element or `null`.\n//\n// The canonical name is the one the developer wrote in the HTML\n// (e.g. `phone`) and the one the backend reads. The hidden input\n// is named `phone` and carries the raw value. It is identified\n// either by the `data-formatter-raw-for` attribute (set when the\n// controller creates the mirror) or by a pre-authored hidden with\n// the same name (no attribute — owned externally, preserved on\n// `destroy()`).\nexport const findHiddenMirror = (\n form: HTMLFormElement,\n fieldName: string\n): HTMLInputElement | null => {\n const byAttr = form.querySelector<HTMLInputElement>(\n `[${FORMATTER_RAW_ATTRIBUTE}=\"${cssEscape(fieldName)}\"]`\n )\n if (byAttr) return byAttr\n\n const byName = form.querySelector<HTMLInputElement>(\n `input[type=\"hidden\"][name=\"${cssEscape(fieldName)}\"]`\n )\n if (byName) return byName\n\n return null\n}\n\n// Create a hidden input that will mirror the raw value of `fieldName`.\n// Idempotent: if a mirror already exists it is returned as-is. The\n// mirror is named with the canonical `fieldName` (not a derived\n// suffix) so the value the backend reads sits at the developer's\n// natural name. The `data-formatter-raw-for` attribute is set so\n// `destroy()` can identify the mirror as owned and remove it.\nexport const ensureHiddenMirror = (\n form: HTMLFormElement,\n fieldName: string\n): HTMLInputElement => {\n const existing = findHiddenMirror(form, fieldName)\n if (existing) return existing\n\n const input = document.createElement('input')\n input.type = 'hidden'\n input.name = fieldName\n input.setAttribute('aria-hidden', 'true')\n input.tabIndex = -1\n input.setAttribute(FORMATTER_RAW_ATTRIBUTE, fieldName)\n form.appendChild(input)\n return input\n}\n\n// Rename a visible input from its current `name` to `displayName`.\n// Idempotent: if the input already has `displayName` it is left\n// alone. Used by `format()` to migrate the developer's authored\n// `<input name=\"phone\">` to `<input name=\"phone_displayed\">` so\n// the canonical `name` slot is free for the hidden raw mirror.\nexport const renameVisibleField = (\n visible: HTMLInputElement | HTMLTextAreaElement,\n displayName: string\n): void => {\n if (visible.name === displayName) return\n visible.name = displayName\n}\n\n// Restore a visible input to `originalName`. Called by the format\n// registry on `destroy()` to put the DOM back the way the developer\n// authored it (modulo the value, which the developer controls).\nexport const restoreVisibleName = (\n visible: HTMLInputElement | HTMLTextAreaElement,\n originalName: string\n): void => {\n visible.name = originalName\n}\n\n// Remove every hidden mirror owned by `format()`. Mirrors created by\n// this controller carry the `data-formatter-raw-for` attribute;\n// mirrors that existed before are left untouched.\nexport const removeOwnedHiddenMirrors = (form: HTMLFormElement): void => {\n form\n .querySelectorAll<HTMLInputElement>(`[${FORMATTER_RAW_ATTRIBUTE}]`)\n .forEach(node => node.remove())\n}\n\n// Write `formatted` into the visible field and `raw` into the hidden\n// mirror, preserving the cursor as best as possible.\n//\n// Returns the new caret position so callers can debug or run additional\n// assertions. The function avoids triggering `MutationObserver` cascades\n// for the hidden mirror by setting `.value` directly (the controller's\n// own delegated handler ignores hidden inputs via `isFieldElement`).\n//\n// `inputType` is the optional `InputEvent.inputType` value (e.g.\n// `'insertText'`, `'deleteContentBackward'`, `'deleteWordBackward'`).\n// When it indicates a deletion, this helper mirrors cleave.js'\n// `postDelimiterBackspace` contract: after the caret is re-anchored in\n// the freshly formatted value, walk backwards while the character\n// immediately to the left of the caret is a format character (delimiter,\n// prefix/suffix symbol, …) and decrement the caret. That keeps the\n// caret from \"jumping\" past a delimiter that disappeared when the user\n// deleted the digit sitting right next to it.\nexport const applyFormattedValue = (\n visible: HTMLInputElement | HTMLTextAreaElement,\n mirror: HTMLInputElement | null,\n formatted: string,\n raw: string,\n inputType?: string\n): number => {\n // Capture the caret in the **raw** input the user just typed — this\n // is what cleave's `getNextCursorPosition` does. Using the formatted\n // previous value would lose information when the user edits in the\n // middle of a masked field (deletion adjacent to delimiters, etc.).\n const rawInput = visible.value\n const caret = clamp(\n visible.selectionStart ?? rawInput.length,\n 0,\n rawInput.length\n )\n\n if (mirror && mirror.value !== raw) mirror.value = raw\n\n if (rawInput === formatted) {\n // The formatter did not change the visible value — usually because\n // the user is editing at the tail of a fully-formatted string. Even\n // so, a backspace can leave the caret sitting with a delimiter on\n // its left (because the digit right after the delimiter just\n // disappeared). Apply the postDelimiterBackspace scan in this path\n // too so the contract holds for both branches.\n const newCaret = pullCaretPastLeadingDelimiter(caret, formatted, inputType)\n if (newCaret !== caret) restoreCursor(visible, newCaret)\n return newCaret\n }\n\n visible.value = formatted\n\n const newCaret = pullCaretPastLeadingDelimiter(\n computeCursorPosition(caret, rawInput, formatted),\n formatted,\n inputType\n )\n restoreCursor(visible, newCaret)\n return newCaret\n}\n\n// Mirrors cleave.js' `postDelimiterBackspace` contract. After a\n// deletion (`inputType` starts with `'delete'`) we never want the\n// caret to sit with a format character immediately on its left; if it\n// would, pull it backwards until it does (or until we hit index 0).\n// No-op when `inputType` is missing or does not indicate a deletion.\nconst pullCaretPastLeadingDelimiter = (\n caret: number,\n value: string,\n inputType: string | undefined\n): number => {\n if (!inputType || !inputType.startsWith('delete')) return caret\n\n let cursor = caret\n while (cursor > 0 && isFormatChar(value[cursor - 1]!)) {\n cursor -= 1\n }\n return cursor\n}\n\nconst clamp = (value: number, min: number, max: number): number =>\n Math.max(min, Math.min(max, value))\n\n// `computeCursorPosition` is a cleave-style implementation:\n// - count \"non-format\" characters (digits / letters) between the caret\n// and the start of the **raw input the user just typed**, then\n// locate that same logical index in the new formatted value\n// (jumping over inserted delimiters).\n// - when the caret is at (or past) the end of the raw input, place\n// it at the end of the formatted value.\n//\n// The algorithm intentionally avoids any cleave-zen dependency so this\n// file is pure DOM arithmetic.\nexport const computeCursorPosition = (\n caretPos: number,\n rawInput: string,\n newValue: string\n): number => {\n if (newValue.length === 0) return 0\n\n // Caret at (or past) the end of the raw input — place at new end.\n if (caretPos >= rawInput.length) return newValue.length\n\n // Caret within the raw input: count non-format characters to its\n // LEFT (i.e. how many real characters are before the caret) and\n // find that same count in the new formatted value.\n const targetNonFormat = countNonFormat(rawInput.slice(0, caretPos))\n\n if (targetNonFormat === 0) {\n // Skip leading delimiters / prefix symbols (`$`, `+`, …) so the\n // caret lands at the first real character of the new value.\n let i = 0\n while (i < newValue.length && isFormatChar(newValue[i]!)) i += 1\n return i\n }\n\n let seen = 0\n for (let i = 0; i < newValue.length; i += 1) {\n if (!isFormatChar(newValue[i]!)) {\n seen += 1\n if (seen === targetNonFormat) {\n return Math.min(i + 1, newValue.length)\n }\n }\n }\n\n return newValue.length\n}\n\nconst countNonFormat = (value: string): number => {\n let count = 0\n for (const char of value) {\n if (!isFormatChar(char)) count += 1\n }\n return count\n}\n\n// \"Format characters\" are delimiters, prefix symbols, and other\n// non-alphanumeric separators that the formatter may insert or remove.\n// We deliberately keep this conservative — anything that is not a digit\n// or ASCII letter counts as a format character.\nexport const isFormatChar = (char: string): boolean => {\n if (!char) return false\n if (char >= '0' && char <= '9') return false\n if ((char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z')) return false\n return true\n}\n\nconst restoreCursor = (\n field: HTMLInputElement | HTMLTextAreaElement,\n position: number\n): void => {\n const max = field.value.length\n const safe = clamp(position, 0, max)\n\n const apply = () => {\n try {\n field.setSelectionRange(safe, safe)\n } catch {\n // Some input types (number, email) throw on setSelectionRange.\n // Ignore — the caret just stays where the browser put it.\n }\n }\n\n // Android keyboard fix: defer one frame so the IME catches up before\n // we re-position the caret. Mirrors cleave.js' `isAndroid` branch.\n if (typeof navigator !== 'undefined' && /Android/i.test(navigator.userAgent)) {\n if (typeof requestAnimationFrame === 'function') {\n requestAnimationFrame(apply)\n } else {\n setTimeout(apply, 1)\n }\n return\n }\n\n apply()\n}\n\n// `getFormatterField` re-exported above is the canonical entry point,\n// but it is intentionally restricted to controller-owned fields. This\n// looser helper accepts a wider set of inputs for test fixtures.\nexport const isFormatterFieldElement = (node: Element): node is FormFieldElement =>\n node instanceof HTMLInputElement ||\n node instanceof HTMLTextAreaElement ||\n node instanceof HTMLSelectElement","// Lazy singleton loader for the optional `@samline/formatter` peer.\n// The formatter is imported dynamically so the package stays usable\n// even when the peer is not installed: callers receive `null` and a\n// single console.error explaining the missing dependency.\n//\n// The first failure is cached so the warning is printed exactly once\n// per process, not on every `format(...)` call.\n\nexport interface FormatterModule {\n format: (\n value: unknown,\n formatType: string,\n options?: Record<string, unknown>\n ) => { formatted: string; raw: string; type: string }\n FORMAT_TYPES?: readonly string[]\n isFormatType?: (value: unknown) => boolean\n regex?: Record<string, { pattern: RegExp; errorMessage: string }>\n}\n\ntype LoaderState =\n | { status: 'pending'; promise: Promise<FormatterModule | null> }\n | { status: 'resolved'; module: FormatterModule | null }\n\nlet state: LoaderState | null = null\n\nconst MISSING_MESSAGE =\n '[samline/forms] The `format()` and `formatAll()` methods require the ' +\n '@samline/formatter package. Install it with: npm i @samline/formatter ' +\n '(or pnpm/bun/yarn equivalent).'\n\nlet warned = false\n\nconst warnOnce = () => {\n if (warned) return\n warned = true\n console.error(MISSING_MESSAGE)\n}\n\nexport const loadFormatter = (): Promise<FormatterModule | null> => {\n if (state && state.status === 'resolved') {\n if (!state.module) warnOnce()\n return Promise.resolve(state.module)\n }\n\n if (state && state.status === 'pending') {\n return state.promise\n }\n\n const promise = (async () => {\n try {\n // Dynamic import keeps the optional peer out of the eager build graph.\n // The consumer's bundler is responsible for resolving the peer at\n // build time — `tsup` treats `@samline/formatter` as `external` so it\n // does not try to pre-bundle it. Do NOT re-add `/* @vite-ignore */`\n // here: in production it leaves the bare specifier as a literal in\n // the bundle, browsers cannot resolve it, and the formatter silently\n // never loads (the catch below masks the failure).\n const mod = (await import('@samline/formatter')) as FormatterModule\n state = { status: 'resolved', module: mod }\n return mod\n } catch {\n warnOnce()\n state = { status: 'resolved', module: null }\n return null\n }\n })()\n\n state = { status: 'pending', promise }\n return promise\n}\n\n// Test-only helper: reset the cached loader so unit tests can swap the\n// dynamic import between \"installed\" and \"missing\" scenarios.\nexport const __resetFormatterLoaderForTests = (): void => {\n state = null\n warned = false\n}\n\n// Test-only helper: inject a formatter implementation directly,\n// bypassing the dynamic import. Pass `null` to simulate the\n// \"peer not installed\" branch.\nexport const __setFormatterModuleForTests = (\n module: FormatterModule | null\n): void => {\n state = { status: 'resolved', module }\n}\n\nexport const __setFormatterPromiseForTests = (\n promise: Promise<FormatterModule | null>\n): void => {\n state = { status: 'pending', promise }\n}\n","import type { FieldFormatConfig, FormController, FormFieldElement } from '../core/types'\nimport {\n applyFormattedValue,\n ensureHiddenMirror,\n findHiddenMirror,\n findVisibleField,\n FORMATTER_RAW_ATTRIBUTE,\n renameVisibleField,\n restoreVisibleName\n} from '../core/format-helpers'\nimport { loadFormatter } from '../core/formatter-loader'\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\n\ntype FormatEntry = {\n canonicalName: string\n displayName: string\n visible: HTMLInputElement | HTMLTextAreaElement\n mirror: HTMLInputElement\n mirrorIsOwned: boolean\n defaultVisible: string | null\n defaultRaw: string | null\n /** Bound after the formatter peer resolves. */\n handler: ((event: Event) => void) | null\n}\n\nconst UNBOUND: ((event: Event) => void) | null = null\n\nconst registry = new WeakMap<FormControllerState, Map<string, FormatEntry>>()\n\nconst getRegistry = (state: FormControllerState): Map<string, FormatEntry> => {\n let bucket = registry.get(state)\n if (!bucket) {\n bucket = new Map<string, FormatEntry>()\n registry.set(state, bucket)\n }\n return bucket\n}\n\nconst resolveFieldNames = (config: FieldFormatConfig): string[] =>\n Array.isArray(config.field) ? config.field : [config.field]\n\nconst addListener = (\n state: FormControllerState,\n target: EventTarget,\n type: string,\n handler: EventListener,\n useCapture?: boolean\n): void => {\n if (useCapture) {\n target.addEventListener(type, handler, { capture: true })\n } else {\n target.addEventListener(type, handler)\n }\n const entry: {\n element: EventTarget\n type: string\n handler: EventListener\n capture?: boolean\n } = { element: target, type, handler }\n if (useCapture !== undefined) entry.capture = useCapture\n state.listeners.push(entry)\n}\n\nconst readInputType = (event: Event): string | undefined => {\n const candidate = event as Partial<InputEvent>\n return typeof candidate.inputType === 'string' ? candidate.inputType : undefined\n}\n\n// Canonical mirrors use auto detection; visible input is always display-order.\nconst buildAutoInterpretedOptions = (\n formatOptions: Record<string, unknown> | undefined\n): Record<string, unknown> => ({\n ...(formatOptions ?? {}),\n interpretInputAs: 'auto'\n})\n\nconst buildDisplayInterpretedOptions = (\n formatOptions: Record<string, unknown> | undefined\n): Record<string, unknown> => ({\n ...(formatOptions ?? {}),\n interpretInputAs: 'display'\n})\n\nconst shouldForceAutoInterpretation = (\n formatOptions: Record<string, unknown> | undefined\n): boolean => formatOptions?.interpretInputAs === undefined\n\nconst buildHandler = (\n visible: HTMLInputElement | HTMLTextAreaElement,\n mirror: HTMLInputElement,\n formatFn: NonNullable<Awaited<ReturnType<typeof loadFormatter>>>['format'],\n formatType: FieldFormatConfig['type'],\n formatOptions: Record<string, unknown> | undefined\n): ((event: Event) => void) => {\n const handler = (event: Event) => {\n if (!visible.isConnected || !mirror.isConnected) return\n\n const isVisible = event.target === visible\n const isMirror = event.target === mirror\n if (!isVisible && !isMirror) return\n\n const rawInput = isMirror ? mirror.value : visible.value\n const inputType = readInputType(event)\n const sourceOptions = shouldForceAutoInterpretation(formatOptions)\n ? isMirror\n ? buildAutoInterpretedOptions(formatOptions)\n : buildDisplayInterpretedOptions(formatOptions)\n : formatOptions\n const { formatted, raw } = formatFn(rawInput, formatType, sourceOptions)\n\n if (!formatted && !raw) {\n visible.value = ''\n if (mirror.value !== '') mirror.value = ''\n return\n }\n\n applyFormattedValue(visible, mirror, formatted, raw, inputType)\n }\n return handler\n}\n\n// Phase one mutates the DOM synchronously; phase two binds only this call's entries.\nconst applyFormat = async (\n state: FormControllerState,\n helpers: FormControllerHelpers,\n config: FieldFormatConfig\n): Promise<void> => {\n if (!state.element || !state.api) return\n if (Array.isArray(config.field) && config.displayField) {\n console.error(\n '[samline/forms] `displayField` can only be used when `field` is a string.'\n )\n return\n }\n\n const formatType = config.type\n const formatOptions = config.options\n const fieldNames = resolveFieldNames(config)\n const bucket = getRegistry(state)\n\n const phase1Entries: FormatEntry[] = []\n\n // Phase 1: establish the visible/canonical pair synchronously.\n for (const fieldName of fieldNames) {\n const displayName = config.displayField ?? `${fieldName}_displayed`\n\n // Accept either a canonical visible field or a pre-authored display field.\n const canonicalFields = helpers.getFieldsByName(fieldName) as FormFieldElement[]\n const candidates = canonicalFields.filter(\n (f): f is HTMLInputElement | HTMLTextAreaElement =>\n (f instanceof HTMLInputElement && f.type !== 'hidden') ||\n f instanceof HTMLTextAreaElement\n )\n let visible: HTMLInputElement | HTMLTextAreaElement | null = candidates[0] ?? null\n if (!visible) {\n visible = findVisibleField(state.element, displayName)\n }\n if (!visible) continue\n\n const existing = bucket.get(fieldName)\n\n let mirror = findHiddenMirror(state.element, fieldName)\n const mirrorIsOwned = !mirror\n if (!mirror) mirror = ensureHiddenMirror(state.element, fieldName)\n\n renameVisibleField(visible, displayName)\n\n if (existing) {\n existing.displayName = displayName\n existing.mirrorIsOwned = mirrorIsOwned\n state.formattedFields.set(fieldName, {\n config,\n displayName,\n mirrorIsOwned\n })\n phase1Entries.push(existing)\n } else {\n const entry: FormatEntry = {\n canonicalName: fieldName,\n displayName,\n visible,\n mirror,\n mirrorIsOwned,\n defaultVisible: null,\n defaultRaw: null,\n handler: UNBOUND\n }\n bucket.set(fieldName, entry)\n state.formattedFields.set(fieldName, {\n config,\n displayName,\n mirrorIsOwned\n })\n phase1Entries.push(entry)\n }\n }\n\n // Phase 2: load the peer, bind listeners, and normalize initial values.\n const formatter = await loadFormatter()\n if (state.isDestroyed) {\n rollbackPhase1(phase1Entries, bucket, state.formattedFields)\n return\n }\n if (!formatter) {\n rollbackPhase1(phase1Entries, bucket, state.formattedFields)\n return\n }\n\n for (const entry of phase1Entries) {\n if (entry.handler !== UNBOUND) {\n // Reformat without duplicating the existing capture listener.\n const current = entry.visible.value\n if (current !== '') {\n const rebindOptions = shouldForceAutoInterpretation(formatOptions)\n ? buildDisplayInterpretedOptions(formatOptions)\n : formatOptions\n const { formatted, raw } = formatter.format(current, formatType, rebindOptions)\n applyFormattedValue(entry.visible, entry.mirror, formatted, raw)\n }\n continue\n }\n\n const handler = buildHandler(\n entry.visible,\n entry.mirror,\n formatter.format,\n formatType,\n formatOptions\n )\n addListener(state, state.element, 'input', handler, true)\n entry.handler = handler\n\n const initial = entry.visible.value\n if (initial !== '') {\n // Initial values may be canonical server data; preserve explicit modes.\n const initialOptions = shouldForceAutoInterpretation(formatOptions)\n ? buildAutoInterpretedOptions(formatOptions)\n : formatOptions\n const { formatted, raw } = formatter.format(initial, formatType, initialOptions)\n applyFormattedValue(entry.visible, entry.mirror, formatted, raw)\n } else if (entry.mirrorIsOwned && entry.mirror.value !== '') {\n entry.mirror.value = ''\n }\n entry.defaultVisible = entry.visible.value\n entry.defaultRaw = entry.mirror.value\n }\n}\n\nexport const resetFormattedFields = (state: FormControllerState): void => {\n const bucket = registry.get(state)\n if (!bucket) return\n for (const entry of bucket.values()) {\n if (entry.defaultVisible !== null) entry.visible.value = entry.defaultVisible\n if (entry.defaultRaw !== null) entry.mirror.value = entry.defaultRaw\n }\n}\n\n// Restore only entries touched by the current call when peer loading fails.\nconst rollbackPhase1 = (\n entries: FormatEntry[],\n bucket: Map<string, FormatEntry>,\n registry: FormControllerState['formattedFields']\n): void => {\n for (const entry of entries) {\n if (entry.visible.isConnected) {\n restoreVisibleName(entry.visible, entry.canonicalName)\n }\n if (entry.mirrorIsOwned && entry.mirror.isConnected) {\n entry.mirror.remove()\n }\n bucket.delete(entry.canonicalName)\n registry.delete(entry.canonicalName)\n }\n}\n\nexport const createFormat =\n (state: FormControllerState, helpers: FormControllerHelpers) =>\n (config: FieldFormatConfig): FormController => {\n if (state.isDestroyed) return state.api!\n if (!state.element) return state.api!\n\n void applyFormat(state, helpers, config)\n\n return state.api!\n }\n\nexport const createFormatAll = createFormat\n\nexport const resolveCanonicalForName = (\n state: FormControllerState,\n name: string\n): string | null => {\n const bucket = registry.get(state)\n if (!bucket) return null\n for (const [canonical, entry] of bucket) {\n if (canonical === name || entry.displayName === name) return canonical\n }\n return null\n}\n\nexport const resolveDisplayNameForName = (\n state: FormControllerState,\n name: string\n): string => {\n const bucket = registry.get(state)\n if (!bucket) return name\n for (const [canonical, entry] of bucket) {\n if (canonical === name) return entry.displayName\n if (entry.displayName === name) return entry.displayName\n }\n return name\n}\n\nexport const cleanupFormatRegistry = (state: FormControllerState): void => {\n if (!state.element) {\n registry.delete(state)\n return\n }\n\n const bucket = registry.get(state)\n if (!bucket) {\n state.element\n .querySelectorAll<HTMLInputElement>(`[${FORMATTER_RAW_ATTRIBUTE}]`)\n .forEach(node => node.remove())\n return\n }\n\n for (const [fieldName, entry] of bucket) {\n if (entry.handler !== UNBOUND) {\n state.element.removeEventListener('input', entry.handler, true)\n }\n if (entry.visible.isConnected) {\n restoreVisibleName(entry.visible, fieldName)\n }\n if (entry.mirrorIsOwned && entry.mirror.isConnected) {\n entry.mirror.remove()\n }\n bucket.delete(fieldName)\n state.formattedFields.delete(fieldName)\n }\n\n // Remove any orphaned mirrors defensively.\n state.element\n .querySelectorAll<HTMLInputElement>(`[${FORMATTER_RAW_ATTRIBUTE}]`)\n .forEach(node => node.remove())\n}\n","// api/destroy.ts\n// Tears down the controller: removes DOM listeners, disconnects the\n// mutation observer, clears caches, and resets internal state.\n// Safe to call multiple times.\n\nimport { cleanupFormatRegistry } from './format'\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\n\nexport const createDestroy =\n (state: FormControllerState, _helpers: FormControllerHelpers) =>\n () => {\n if (state.isDestroyed) return\n\n state.isDestroyed = true\n for (const listener of state.listeners) {\n const options: EventListenerOptions | undefined = listener.capture\n ? { capture: true }\n : undefined\n if (options) {\n listener.element.removeEventListener(listener.type, listener.handler, options)\n } else {\n listener.element.removeEventListener(listener.type, listener.handler)\n }\n }\n state.listeners.length = 0\n\n if (state.autoSubmitTimer) {\n clearTimeout(state.autoSubmitTimer)\n state.autoSubmitTimer = null\n }\n\n state.mutationObserver?.disconnect()\n state.mutationObserver = null\n\n const cleanups = Array.from(state.cleanups).reverse()\n state.cleanups.clear()\n for (const cleanup of cleanups) {\n try {\n cleanup()\n } catch (error) {\n console.error('[forms] cleanup failed', error)\n }\n }\n\n state.watchedFields.clear()\n state.subscribers.clear()\n state.submitHandlers.clear()\n state.fieldCache.clear()\n state.manualErrors = {}\n state.validationErrors = {}\n state.validationGroupErrors = {}\n state.validationElementErrors.clear()\n state.pendingSubmitCount = 0\n\n // Detach format listeners and drop the hidden raw mirrors that\n // `format()` created for this controller. Mirrors that pre-existed\n // in the DOM (i.e. not owned by us) are preserved.\n cleanupFormatRegistry(state)\n }\n","// api/disable-auto-submit.ts\n// Disables autoSubmit and cancels any pending debounce timer.\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\n\nexport const createDisableAutoSubmit =\n (state: FormControllerState, helpers: FormControllerHelpers) =>\n () => {\n state.autoSubmitEnabled = false\n if (state.autoSubmitTimer) {\n clearTimeout(state.autoSubmitTimer)\n state.autoSubmitTimer = null\n }\n helpers.notifySubscribers()\n return state.api!\n }\n","// Pure serialization helpers for HTMLFormElement.\n// No DOM mutation, no controller state — safe to tree-shake into any bundle.\n\nimport type { SerializedFormResult, SerializedFormValue } from './types'\n\nconst isEmptyFile = (value: FormDataEntryValue): value is File =>\n value instanceof File && value.size === 0 && value.name === ''\n\nconst appendValue = (\n data: Record<string, SerializedFormValue>,\n key: string,\n value: FormDataEntryValue\n): void => {\n if (!Object.prototype.hasOwnProperty.call(data, key)) {\n Object.defineProperty(data, key, {\n value,\n writable: true,\n enumerable: true,\n configurable: true\n })\n return\n }\n const current = data[key] as SerializedFormValue\n if (Array.isArray(current)) {\n current.push(value)\n return\n }\n data[key] = [current, value]\n}\n\nexport const parseFormData = (\n formElement: HTMLFormElement,\n submitter?: HTMLElement | null\n): SerializedFormResult => {\n const raw = submitter\n ? new FormData(formElement, submitter)\n : new FormData(formElement)\n const formData = new FormData()\n const data: Record<string, SerializedFormValue> = {}\n\n raw.forEach((value, key) => {\n if (isEmptyFile(value)) return\n formData.append(key, value)\n appendValue(data, key, value)\n })\n\n return { data, formData }\n}\n","// api/get-data.ts\n// Returns the form serialized as both a plain object and a fresh\n// FormData instance. Empty file inputs are filtered out.\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\nimport type { SerializedFormResult } from '../core/types'\nimport { parseFormData } from '../core/serialize'\n\nexport const createGetData =\n (_state: FormControllerState, _helpers: FormControllerHelpers) =>\n (): SerializedFormResult => {\n if (!_state.element) return { data: {}, formData: new FormData() }\n return parseFormData(_state.element)\n }\n","// api/get-field.ts\n// Returns the underlying DOM field(s) for a given name:\n// - null when no field matches\n// - a single element when there's exactly one\n// - an array when there are multiple (radios, checkbox groups)\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\nimport type { FormFieldElement } from '../core/types'\n\nexport const createGetField =\n (state: FormControllerState, helpers: FormControllerHelpers) =>\n (name: string): FormFieldElement | FormFieldElement[] | null => {\n const fields = helpers.getFieldsByName(name)\n if (fields.length === 0) return null\n return fields.length === 1 ? fields[0]! : fields\n }\n","// Shared mutable state for the controller plus the small pure helpers used\n// by both core utilities and the api method factories.\n\nimport type {\n FieldFormatConfig,\n FormController,\n FormControllerOptions,\n FormCleanup,\n FormErrors,\n FormFieldElement,\n FormFieldWatcher,\n FormStateListener,\n FormStateSnapshot,\n FormValues,\n ValidationResult,\n ValidationSchema,\n VisualAttributes\n} from './types'\n\nexport interface FormControllerState {\n element: HTMLFormElement | null\n options: FormControllerOptions\n attributes: Required<VisualAttributes>\n validators: ValidationSchema\n watchedFields: Map<string, Set<FormFieldWatcher>>\n subscribers: Set<FormStateListener>\n submitHandlers: Set<{\n callback: import('./types').FormSubmitHandler\n preventDefault: boolean\n }>\n fieldCache: Map<string, FormFieldElement[]>\n manualErrors: FormErrors\n validationErrors: FormErrors\n validationGroupErrors: FormErrors\n validationElementErrors: Map<string, Map<FormFieldElement, string[]>>\n isValidated: boolean\n autoSubmitEnabled: boolean\n autoSubmitDebounce: number\n submitCount: number\n pendingSubmitCount: number\n autoSubmitTimer: ReturnType<typeof setTimeout> | null\n isDestroyed: boolean\n listeners: Array<{\n element: EventTarget\n type: string\n handler: EventListenerOrEventListenerObject\n /**\n * `true` when the listener was registered with `capture: true`.\n * Required for matching `removeEventListener` on teardown —\n * the spec treats capture and bubble as distinct registrations.\n */\n capture?: boolean\n }>\n mutationObserver: MutationObserver | null\n cleanups: Set<FormCleanup>\n // Tracks every field that `format()` is currently formatting, so\n // re-bindings are idempotent and `destroy()` can clean up the\n // associated listeners + hidden raw mirror. Keyed by the canonical\n // field name (the one the developer wrote in the HTML and the one\n // the backend reads). `displayName` is the visible input's name\n // (defaults to `${canonical}_displayed`).\n formattedFields: Map<\n string,\n {\n config: FieldFormatConfig\n displayName: string\n mirrorIsOwned: boolean\n }\n >\n // Back-reference to the assembled public controller. Populated by the\n // controller orchestrator after the api method factories are wired.\n api: FormController | null\n}\n\n// Internal helpers bound by the controller orchestrator and consumed by\n// the api method factories. Kept here so api/*.ts only depends on the\n// shape, not on the orchestrator implementation.\nexport interface FormControllerHelpers {\n notifySubscribers: () => void\n clearFieldCache: () => void\n getFieldsByName: (name: string) => FormFieldElement[]\n getTrackedFieldNames: () => string[]\n getValues: () => FormValues\n getMergedErrors: () => FormErrors\n syncVisualState: (names?: string[]) => void\n validateNames: (names?: string[]) => ValidationResult\n emitFieldWatchers: (name: string) => void\n scheduleAutoSubmit: () => void\n}\n\nexport const createEmptyFormState = (): FormStateSnapshot => ({\n values: {},\n errors: {},\n filledFields: [],\n isValid: true,\n isValidated: false,\n autoSubmit: false,\n isSubmitting: false,\n submitCount: 0\n})\n\nexport const cloneErrors = (errors: FormErrors): FormErrors =>\n Object.fromEntries(\n Object.entries(errors).map(([field, messages]) => [field, [...messages]])\n )\n\nexport const mergeErrors = (left: FormErrors, right: FormErrors): FormErrors => {\n const merged: FormErrors = { ...left }\n for (const [field, messages] of Object.entries(right)) {\n merged[field] = merged[field] ? [...merged[field], ...messages] : [...messages]\n }\n return merged\n}\n","// api/get-state.ts\n// Builds a snapshot of values, errors, filled fields and submission\n// metadata. Pure read — does not mutate controller state.\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\nimport type { FormStateSnapshot } from '../core/types'\nimport { cloneErrors, createEmptyFormState } from '../core/state'\n\nexport const createGetState =\n (state: FormControllerState, helpers: FormControllerHelpers) =>\n (): FormStateSnapshot => {\n const values = helpers.getValues()\n const errors = cloneErrors(helpers.getMergedErrors())\n const filledFields = helpers.getTrackedFieldNames().filter(name => {\n const value = values[name]\n if (Array.isArray(value)) return value.length > 0\n if (typeof value === 'string') return value.trim().length > 0\n return value !== undefined\n })\n\n return {\n ...createEmptyFormState(),\n values,\n errors,\n filledFields,\n isValid: Object.keys(errors).length === 0,\n isValidated: state.isValidated,\n autoSubmit: state.autoSubmitEnabled,\n isSubmitting: state.pendingSubmitCount > 0,\n submitCount: state.submitCount\n }\n }\n","// Low-level DOM helpers used by the controller orchestrator and by api\n// methods that need direct DOM access. Pure: no controller state.\n\nimport type { FormFieldElement, FormFieldValue, FormTarget } from './types'\n\nconst isFormFieldElement = (value: Element): value is FormFieldElement =>\n value instanceof HTMLInputElement ||\n value instanceof HTMLSelectElement ||\n value instanceof HTMLTextAreaElement\n\n// Convention: a name ending with \"[]\" signals multi-value inputs (the\n// de facto HTML form pattern used by PHP, Rails, Express, Spring, etc.\n// to collect repeated inputs as an array on the server). The `[]`\n// suffix is the only signal the controller uses; bare names keep the\n// original first-value semantics.\nconst isArraySyntax = (field: FormFieldElement): boolean =>\n typeof field.name === 'string' && field.name.endsWith('[]')\n\nexport const resolveFormElement = (target: FormTarget): HTMLFormElement | null => {\n if (!target) return null\n if (target instanceof HTMLFormElement) return target\n if (typeof target === 'string') {\n const node = document.getElementById(target)\n return node instanceof HTMLFormElement ? node : null\n }\n if ('current' in target) {\n return target.current instanceof HTMLFormElement ? target.current : null\n }\n return null\n}\n\nexport const getNamedFields = (form: HTMLFormElement): FormFieldElement[] =>\n Array.from(form.elements).filter(\n (element): element is FormFieldElement =>\n isFormFieldElement(element) && Boolean(element.name)\n )\n\nexport const queryNamedFields = (\n form: HTMLFormElement,\n name: string\n): FormFieldElement[] => getNamedFields(form).filter(field => field.name === name)\n\nexport const isFieldElement = (value: Element): value is FormFieldElement =>\n isFormFieldElement(value)\n\nexport const isFieldFilled = (field: FormFieldElement): boolean => {\n if (field instanceof HTMLInputElement) {\n if (field.type === 'checkbox' || field.type === 'radio') return field.checked\n if (field.type === 'file') return Boolean(field.files && field.files.length > 0)\n return field.value.trim() !== ''\n }\n return field.value !== ''\n}\n\nexport const applyBooleanAttribute = (\n field: FormFieldElement,\n attribute: string,\n enabled: boolean\n): void => {\n if (enabled) {\n field.setAttribute(attribute, '')\n } else {\n field.removeAttribute(attribute)\n }\n}\n\nexport const readFieldValue = (fields: FormFieldElement[]): FormFieldValue => {\n if (fields.length === 0) return undefined\n const first = fields[0]\n if (!first) return undefined\n\n // Multi-value inputs: name=\"foo[]\". Only treat the group as an array\n // when there is more than one matching input — a single input with a\n // `[]` suffix still returns a plain string, matching the non-array\n // semantics and avoiding a one-element array that callers would\n // have to flatten.\n if (isArraySyntax(first) && fields.length > 1) {\n if (first instanceof HTMLSelectElement || first instanceof HTMLTextAreaElement) {\n return fields.map(f => f.value)\n }\n\n if (first instanceof HTMLInputElement) {\n if (first.type === 'radio') {\n // Radio groups stay single-valued even with the `[]` suffix —\n // they semantically mean \"one of these\". Find the checked one.\n const checked = fields.find(\n (f): f is HTMLInputElement =>\n f instanceof HTMLInputElement && f.checked\n )\n return checked?.value ?? ''\n }\n\n if (first.type === 'checkbox') {\n // Always return an array of checked values (no single-value\n // collapse) so `Array.isArray(value)` validators stay correct.\n return fields\n .filter(\n (f): f is HTMLInputElement =>\n f instanceof HTMLInputElement && f.checked\n )\n .map(f => f.value)\n }\n\n if (first.type === 'file') {\n // Concatenate every selected file across all matching inputs.\n return fields.flatMap(f =>\n f instanceof HTMLInputElement && f.files ? Array.from(f.files) : []\n )\n }\n }\n\n // text / email / password / number / hidden — one string per input.\n return fields.map(f => f.value)\n }\n\n if (first instanceof HTMLSelectElement && first.multiple) {\n return Array.from(first.selectedOptions, option => option.value)\n }\n\n if (first instanceof HTMLSelectElement || first instanceof HTMLTextAreaElement) {\n return first.value\n }\n\n if (first instanceof HTMLInputElement) {\n if (first.type === 'radio') {\n const checked = fields.find(\n (f): f is HTMLInputElement =>\n f instanceof HTMLInputElement && f.checked\n )\n return checked?.value ?? ''\n }\n\n if (first.type === 'checkbox') {\n const values = fields\n .filter(\n (f): f is HTMLInputElement =>\n f instanceof HTMLInputElement && f.checked\n )\n .map(f => f.value)\n return values.length > 1 ? values : (values[0] ?? '')\n }\n\n if (first.type === 'file') {\n return first.files ? Array.from(first.files) : []\n }\n }\n\n return first.value\n}\n\nexport const writeFieldValue = (fields: FormFieldElement[], value: unknown): void => {\n const normalizedArray = Array.isArray(value)\n ? value.map(item => String(item))\n : null\n const normalizedValue = value === undefined || value === null ? '' : String(value)\n\n // For `name=\"foo[]\"` fields receiving an array, distribute one element\n // per input. Surplus inputs (more inputs than array elements) are\n // cleared to ''; surplus array elements (more elements than inputs)\n // are dropped — there's no input to write to. The distribution only\n // applies to text-like inputs; checkbox / radio / file keep their\n // own special semantics.\n const distributeArray =\n fields.length > 0 &&\n fields[0] !== undefined &&\n isArraySyntax(fields[0]) &&\n normalizedArray !== null\n\n for (let index = 0; index < fields.length; index++) {\n const field = fields[index]\n if (!field) continue\n\n if (field instanceof HTMLInputElement) {\n if (field.type === 'checkbox') {\n field.checked = normalizedArray\n ? normalizedArray.includes(field.value)\n : normalizedValue === field.value\n continue\n }\n if (field.type === 'radio') {\n field.checked = field.value === normalizedValue\n continue\n }\n if (field.type === 'file') {\n if (Array.isArray(value) && value.length === 0) field.value = ''\n continue\n }\n if (distributeArray && normalizedArray) {\n field.value = normalizedArray[index] ?? ''\n continue\n }\n field.value = normalizedValue\n continue\n }\n\n if (field instanceof HTMLSelectElement && field.multiple) {\n const selectedValues = normalizedArray ?? [normalizedValue]\n for (const option of field.options) {\n option.selected = selectedValues.includes(option.value)\n }\n continue\n }\n\n if (distributeArray && normalizedArray) {\n field.value = normalizedArray[index] ?? ''\n continue\n }\n\n field.value = normalizedValue\n }\n}\n\nexport const clearAttributes = (form: HTMLFormElement, attributes: string[]): void => {\n for (const attribute of attributes) {\n getNamedFields(form).forEach(field => field.removeAttribute(attribute))\n }\n}\n\nexport const submitForm = (form: HTMLFormElement): void => {\n if (typeof form.requestSubmit === 'function') {\n form.requestSubmit()\n return\n }\n const submitButton = form.querySelector(\n 'button[type=\"submit\"], input[type=\"submit\"]'\n ) as HTMLButtonElement | HTMLInputElement | null\n if (submitButton) {\n submitButton.click()\n return\n }\n form.submit()\n}\n","// api/get-value.ts\n// Returns the current value of a field, with normalization for checkboxes,\n// radios, files, and multi-value inputs.\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\nimport type { FormFieldValue } from '../core/types'\nimport { readFieldValue } from '../core/dom'\n\nexport const createGetValue =\n (state: FormControllerState, helpers: FormControllerHelpers) =>\n (name: string): FormFieldValue => readFieldValue(helpers.getFieldsByName(name))\n","// api/observe.ts\n// Registers a callback that runs on mount and whenever the field changes.\n// Returns an unsubscribe function.\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\nimport type { FormFieldWatcher } from '../core/types'\n\nexport const createObserve =\n (state: FormControllerState, helpers: FormControllerHelpers) =>\n (field: string, callback: FormFieldWatcher) => {\n const callbacks = state.watchedFields.get(field) ?? new Set<FormFieldWatcher>()\n callbacks.add(callback)\n state.watchedFields.set(field, callbacks)\n\n if (state.element) {\n callback(state.api!.getValue(field), state.api!.getField(field), state.element, state.api!.getState())\n }\n\n return () => {\n const current = state.watchedFields.get(field)\n current?.delete(callback)\n if (current && current.size === 0) {\n state.watchedFields.delete(field)\n }\n }\n }\n","// api/on-submit.ts\n// Registers a submit handler. preventDefault=true intercepts valid submits,\n// which is the right choice for AJAX/fetch flows. Invalid submits are\n// always prevented regardless of the flag.\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\nimport type { FormSubmitHandler } from '../core/types'\n\nexport const createOnSubmit =\n (state: FormControllerState, _helpers: FormControllerHelpers) =>\n (callback: FormSubmitHandler, preventDefault = true) => {\n state.submitHandlers.add({ callback, preventDefault })\n return state.api!\n }\n","// api/prefill.ts\n// Reads window.location.search and writes matching values into the form.\n// Pass a field name to scope prefill to a single field.\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\n\nexport const createPrefill =\n (state: FormControllerState, _helpers: FormControllerHelpers) =>\n (fieldName?: string) => {\n if (!state.element) return state.api!\n\n const params = new URLSearchParams(window.location.search)\n params.forEach((value, key) => {\n if (fieldName && fieldName !== key) return\n state.api!.setValue(key, value)\n })\n return state.api!\n }\n","// api/reset.ts\n// Resets the native form, clears manual and validation errors, strips\n// css-filled/css-error attributes, and notifies subscribers.\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\nimport { clearAttributes } from '../core/dom'\nimport { resetFormattedFields } from './format'\n\nexport const createReset =\n (state: FormControllerState, helpers: FormControllerHelpers) =>\n () => {\n if (!state.element) return state.api!\n\n state.element.reset()\n resetFormattedFields(state)\n state.manualErrors = {}\n state.validationErrors = {}\n state.validationGroupErrors = {}\n state.validationElementErrors.clear()\n clearAttributes(state.element, [\n state.attributes.error,\n state.attributes.filled,\n 'aria-invalid'\n ])\n\n helpers.syncVisualState()\n helpers.notifySubscribers()\n return state.api!\n }\n","// api/validate.ts\n// Runs validation. Marks the form as validated, syncs visual state, and\n// returns the validation result for callers that want it.\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\nimport type { ValidationResult } from '../core/types'\n\nexport const createValidate =\n (state: FormControllerState, helpers: FormControllerHelpers) =>\n (fields?: string[]): ValidationResult => {\n state.isValidated = true\n helpers.syncVisualState(fields)\n return helpers.validateNames(fields)\n }\n","// api/revalidate.ts\n// Alias of validate(); kept separate for readability at call sites that\n// want to express \"re-run validation now\".\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\nimport { createValidate } from './validate'\n\nexport const createRevalidate = createValidate\n","// api/set-errors.ts\n// Sets manual errors. Accepts an array of field names (each gets a\n// generic message) or a FormErrors map with per-field messages.\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\nimport type { FormErrors } from '../core/types'\nimport { cloneErrors } from '../core/state'\n\nexport const createSetErrors =\n (state: FormControllerState, helpers: FormControllerHelpers) =>\n (fields: string[] | FormErrors) => {\n if (Array.isArray(fields)) {\n state.manualErrors = {\n ...state.manualErrors,\n ...Object.fromEntries(fields.map(field => [field, ['Invalid value.']]))\n }\n } else {\n state.manualErrors = {\n ...state.manualErrors,\n ...cloneErrors(fields)\n }\n }\n\n helpers.syncVisualState(Array.isArray(fields) ? fields : Object.keys(fields))\n helpers.notifySubscribers()\n return state.api!\n }\n","// api/set-value.ts\n// Writes a value into a field and dispatches the correct DOM event so\n// listeners, validators, and visual state run as if a user typed.\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\nimport { writeFieldValue } from '../core/dom'\n\nexport const createSetValue =\n (state: FormControllerState, helpers: FormControllerHelpers) =>\n (name: string, value: unknown) => {\n const fields = helpers.getFieldsByName(name)\n if (fields.length === 0) return state.api!\n\n writeFieldValue(fields, value)\n const firstField = fields[0]\n if (!firstField) return state.api!\n\n firstField.dispatchEvent(new Event('input', { bubbles: true }))\n return state.api!\n }\n","// api/subscribe.ts\n// Subscribes to global form state. Listener fires once immediately with\n// the current snapshot, then on every state mutation. Returns an\n// unsubscribe function.\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\nimport type { FormStateListener } from '../core/types'\n\nexport const createSubscribe =\n (state: FormControllerState, _helpers: FormControllerHelpers) =>\n (listener: FormStateListener) => {\n state.subscribers.add(listener)\n listener(state.api!.getState())\n return () => {\n state.subscribers.delete(listener)\n }\n }\n","// api/unwatch.ts\n// Removes watched callbacks. With no args, clears every watcher. With only\n// a field, removes all callbacks for that field. With field + callback,\n// removes just that callback.\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\nimport type { FormFieldWatcher } from '../core/types'\n\nexport const createUnwatch =\n (state: FormControllerState, _helpers: FormControllerHelpers) =>\n (field?: string, callback?: FormFieldWatcher) => {\n if (!field) {\n state.watchedFields.clear()\n return state.api!\n }\n\n if (!callback) {\n state.watchedFields.delete(field)\n return state.api!\n }\n\n const callbacks = state.watchedFields.get(field)\n callbacks?.delete(callback)\n if (callbacks && callbacks.size === 0) {\n state.watchedFields.delete(field)\n }\n return state.api!\n }\n","// api/watch.ts\n// Chainable alias over `observe`. Use this when you want a fluent setup\n// without holding the returned unsubscribe function.\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\nimport type { FormFieldWatcher } from '../core/types'\nimport { createObserve } from './observe'\n\nexport const createWatch = (state: FormControllerState, helpers: FormControllerHelpers) =>\n (field: string, callback: FormFieldWatcher) => {\n const observe = createObserve(state, helpers)\n observe(field, callback)\n return state.api!\n }\n","// Pure validation routines. No DOM, no controller state.\n\nimport type {\n FieldValidationContext,\n FieldValidationRules,\n FormErrors,\n FormFieldValue,\n FormValues,\n RuleConfig,\n ValueValidationRules,\n ValidationResult,\n ValidationSchema\n} from './types'\n\nconst resolveRule = <T>(rule: RuleConfig<T> | undefined) => {\n if (rule === undefined) {\n return { value: undefined as T | undefined, message: undefined as string | undefined }\n }\n if (typeof rule === 'object' && rule !== null && 'value' in rule) {\n return { value: rule.value, message: rule.message }\n }\n return { value: rule, message: undefined as string | undefined }\n}\n\nconst hasValue = (value: FormFieldValue): boolean => {\n if (Array.isArray(value)) return value.length > 0\n if (typeof value === 'string') return value.trim().length > 0\n return value !== undefined\n}\n\nconst getValueLength = (value: FormFieldValue): number => {\n if (Array.isArray(value)) return value.length\n if (typeof value === 'string') return value.length\n return 0\n}\n\nconst toPatternTarget = (value: FormFieldValue): string => {\n if (typeof value === 'string') return value\n if (Array.isArray(value)) {\n return value\n .map(entry => (typeof entry === 'string' ? entry : entry.name))\n .join(',')\n }\n return ''\n}\n\nconst valuesAreEqual = (left: FormFieldValue, right: FormFieldValue): boolean => {\n if (Array.isArray(left) && Array.isArray(right)) {\n return left.length === right.length && left.every((entry, index) => entry === right[index])\n }\n return left === right\n}\n\nconst DECIMAL_PATTERN = /^[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)$/\n\nconst parseStrictNumber = (value: FormFieldValue): number | undefined => {\n if (typeof value !== 'string') return undefined\n const candidate = value.trim()\n if (!DECIMAL_PATTERN.test(candidate)) return undefined\n const number = Number(candidate)\n return Number.isFinite(number) ? number : undefined\n}\n\nexport interface FieldValidationItem {\n value: FormFieldValue\n element?: import('./types').FormFieldElement\n}\n\nexport interface DetailedValidationResult {\n groupErrors: string[]\n itemErrors: Array<{\n index: number\n element?: import('./types').FormFieldElement\n messages: string[]\n }>\n}\n\nconst validateValue = (\n field: string,\n value: FormFieldValue,\n rules: ValueValidationRules,\n values: FormValues,\n contextOverrides: Partial<Pick<FieldValidationContext, 'element' | 'index'>> = {}\n): string[] => {\n const errors: string[] = []\n const context: FieldValidationContext = {\n field,\n value,\n values,\n ...contextOverrides\n }\n const required = resolveRule(rules.required)\n const minLength = resolveRule(rules.minLength)\n const maxLength = resolveRule(rules.maxLength)\n const pattern = resolveRule(rules.pattern)\n const numeric = resolveRule(rules.numeric)\n const min = resolveRule(rules.min)\n const max = resolveRule(rules.max)\n\n if (required.value && !hasValue(value)) {\n errors.push(required.message ?? 'This field is required.')\n }\n if (minLength.value !== undefined && getValueLength(value) < minLength.value) {\n errors.push(minLength.message ?? `Minimum length is ${minLength.value}.`)\n }\n if (maxLength.value !== undefined && getValueLength(value) > maxLength.value) {\n errors.push(maxLength.message ?? `Maximum length is ${maxLength.value}.`)\n }\n if (pattern.value && hasValue(value)) {\n pattern.value.lastIndex = 0\n const matches = pattern.value.test(toPatternTarget(value))\n pattern.value.lastIndex = 0\n if (!matches) {\n errors.push(pattern.message ?? 'Value does not match the required pattern.')\n }\n }\n\n if (hasValue(value) && (numeric.value || min.value !== undefined || max.value !== undefined)) {\n const number = parseStrictNumber(value)\n if (number === undefined) {\n errors.push(numeric.message ?? 'Value must be a number.')\n } else {\n if (min.value !== undefined && number < min.value) {\n errors.push(min.message ?? `Minimum value is ${min.value}.`)\n }\n if (max.value !== undefined && number > max.value) {\n errors.push(max.message ?? `Maximum value is ${max.value}.`)\n }\n }\n }\n\n const custom = rules.validate\n ? Array.isArray(rules.validate)\n ? rules.validate\n : [rules.validate]\n : []\n\n for (const validator of custom) {\n const result = validator(context)\n if (typeof result === 'string' && result.length > 0) errors.push(result)\n if (result === false) errors.push('Validation failed.')\n }\n\n return errors\n}\n\nexport const validateFieldValueDetailed = (\n field: string,\n value: FormFieldValue,\n rules: FieldValidationRules,\n values: FormValues,\n items?: FieldValidationItem[]\n): DetailedValidationResult => {\n const groupErrors = validateValue(field, value, rules, values)\n const sameAs = resolveRule(rules.sameAs)\n\n if (sameAs.value !== undefined) {\n const otherValue = values[sameAs.value]\n if (hasValue(value) && hasValue(otherValue) && !valuesAreEqual(value, otherValue)) {\n groupErrors.push(sameAs.message ?? `Value must match ${sameAs.value}.`)\n }\n }\n\n const itemErrors: DetailedValidationResult['itemErrors'] = []\n if (rules.each) {\n const sourceItems: FieldValidationItem[] =\n items ??\n (Array.isArray(value)\n ? value.map(entry => ({\n value: typeof entry === 'string' ? entry : ([entry] as File[])\n }))\n : [{ value }])\n\n sourceItems.forEach((item, index) => {\n const overrides: Partial<Pick<FieldValidationContext, 'element' | 'index'>> = {\n index\n }\n if (item.element) overrides.element = item.element\n const messages = validateValue(field, item.value, rules.each!, values, overrides)\n if (messages.length === 0) return\n const error: DetailedValidationResult['itemErrors'][number] = {\n index,\n messages\n }\n if (item.element) error.element = item.element\n itemErrors.push(error)\n })\n }\n\n return { groupErrors, itemErrors }\n}\n\nexport const validateFieldValue = (\n field: string,\n value: FormFieldValue,\n rules: FieldValidationRules,\n values: FormValues\n): string[] => {\n const result = validateFieldValueDetailed(field, value, rules, values)\n return [...result.groupErrors, ...result.itemErrors.flatMap(item => item.messages)]\n}\n\nexport const validateValues = (\n values: FormValues,\n schema: ValidationSchema\n): ValidationResult => {\n const errors: FormErrors = {}\n for (const [field, rules] of Object.entries(schema)) {\n const messages = validateFieldValue(field, values[field], rules, values)\n if (messages.length > 0) errors[field] = messages\n }\n return {\n isValid: Object.keys(errors).length === 0,\n errors\n }\n}\n","// Core orchestrator. Wires shared state, internal helpers and the public\n// api method factories into a single FormController instance.\n//\n// Internal event handlers and DOM wiring live here. Every public method\n// lives in `../api/<method>.ts` as an isolated factory.\n\nimport { createAppend } from '../api/append'\nimport { createAddCleanup } from '../api/add-cleanup'\nimport { createAutoSubmit } from '../api/auto-submit'\nimport { createClearErrors } from '../api/clear-errors'\nimport { createDestroy } from '../api/destroy'\nimport { createDisableAutoSubmit } from '../api/disable-auto-submit'\nimport {\n createFormat,\n createFormatAll,\n resolveCanonicalForName,\n resolveDisplayNameForName\n} from '../api/format'\nimport { createGetData } from '../api/get-data'\nimport { createGetField } from '../api/get-field'\nimport { createGetState } from '../api/get-state'\nimport { createGetValue } from '../api/get-value'\nimport { createObserve } from '../api/observe'\nimport { createOnSubmit } from '../api/on-submit'\nimport { createPrefill } from '../api/prefill'\nimport { createReset } from '../api/reset'\nimport { createRevalidate } from '../api/revalidate'\nimport { createSetErrors } from '../api/set-errors'\nimport { createSetValue } from '../api/set-value'\nimport { createSubscribe } from '../api/subscribe'\nimport { createUnwatch } from '../api/unwatch'\nimport { createValidate } from '../api/validate'\nimport { createWatch } from '../api/watch'\nimport { validateFieldValueDetailed } from '../core/validation'\nimport { parseFormData } from './serialize'\nimport {\n applyBooleanAttribute,\n getNamedFields,\n isFieldElement,\n isFieldFilled,\n queryNamedFields,\n readFieldValue,\n resolveFormElement,\n submitForm,\n writeFieldValue\n} from './dom'\nimport { cloneErrors, mergeErrors } from './state'\nimport type {\n FormController,\n FormControllerOptions,\n FormFieldElement,\n FormFieldValue,\n FormTarget,\n ValidationResult,\n VisualAttributes\n} from './types'\nimport type { FormControllerHelpers, FormControllerState } from './state'\n\nconst DEFAULT_ATTRIBUTES: Required<VisualAttributes> = {\n filled: 'css-filled',\n error: 'css-error'\n}\n\nconst DEFAULT_OPTIONS = {\n autoValidate: true,\n clearErrorsOnSubmit: true,\n clearManualErrorsOnChange: true\n} satisfies Pick<\n FormControllerOptions,\n 'autoValidate' | 'clearErrorsOnSubmit' | 'clearManualErrorsOnChange'\n>\n\nexport const createFormController = (\n target: FormTarget,\n options: FormControllerOptions = {}\n): FormController => {\n const element = resolveFormElement(target)\n const attributes = { ...DEFAULT_ATTRIBUTES, ...options.attributes }\n const normalizedOptions: FormControllerOptions = {\n ...DEFAULT_OPTIONS,\n ...options,\n attributes\n }\n\n const state: FormControllerState = {\n element,\n options: normalizedOptions,\n attributes,\n validators: normalizedOptions.validators ?? {},\n watchedFields: new Map(),\n subscribers: new Set(),\n submitHandlers: new Set(),\n fieldCache: new Map(),\n manualErrors: {},\n validationErrors: {},\n validationGroupErrors: {},\n validationElementErrors: new Map(),\n isValidated: Boolean(normalizedOptions.autoValidate),\n autoSubmitEnabled: false,\n autoSubmitDebounce: 0,\n submitCount: 0,\n pendingSubmitCount: 0,\n autoSubmitTimer: null,\n isDestroyed: false,\n listeners: [],\n mutationObserver: null,\n cleanups: new Set(),\n formattedFields: new Map(),\n api: null\n }\n\n // Reverse cross-field references once so source changes can revalidate\n // dependent fields without installing per-field listeners.\n const validationDependents = new Map<string, Set<string>>()\n for (const [field, rules] of Object.entries(state.validators)) {\n const sources = new Set<string>()\n const sameAs = rules.sameAs\n if (sameAs !== undefined) {\n sources.add(typeof sameAs === 'string' ? sameAs : sameAs.value)\n }\n if (typeof rules.dependsOn === 'string') sources.add(rules.dependsOn)\n else rules.dependsOn?.forEach(source => sources.add(source))\n\n for (const source of sources) {\n const dependents = validationDependents.get(source) ?? new Set<string>()\n dependents.add(field)\n validationDependents.set(source, dependents)\n }\n }\n\n // ------- Internal helpers (no DOM wiring of their own) -----------------\n\n const notifySubscribers = () => {\n if (!state.api) return\n const snapshot = state.api.getState()\n state.subscribers.forEach(listener => listener(snapshot))\n }\n\n const addListener = (\n node: EventTarget,\n type: string,\n handler: EventListenerOrEventListenerObject,\n capture?: boolean\n ) => {\n if (capture) {\n node.addEventListener(type, handler, { capture: true })\n } else {\n node.addEventListener(type, handler)\n }\n // `exactOptionalPropertyTypes: true` rejects explicit `undefined`\n // values for optional properties; only attach `capture` when it\n // actually has a value.\n const entry: {\n element: EventTarget\n type: string\n handler: EventListenerOrEventListenerObject\n capture?: boolean\n } = { element: node, type, handler }\n if (capture !== undefined) entry.capture = capture\n state.listeners.push(entry)\n }\n\n const clearFieldCache = () => state.fieldCache.clear()\n\n const getFieldsByName = (name: string): FormFieldElement[] => {\n if (!state.element) return []\n const fields = queryNamedFields(state.element, name)\n state.fieldCache.set(name, fields)\n return fields\n }\n\n const getTrackedFieldNames = (): string[] => {\n if (!state.element) return Object.keys(state.validators)\n const names = new Set<string>(Object.keys(state.validators))\n for (const field of getNamedFields(state.element)) {\n names.add(field.name)\n }\n // The formatted registry keeps the canonical name as the bucket\n // key. The display name (e.g. `phone_displayed`) is also a\n // tracked field because the developer can `watch`/`getValue` it\n // and `getData()` exposes it in the serialized payload.\n for (const [canonical, entry] of state.formattedFields) {\n names.add(canonical)\n if (entry.displayName && entry.displayName !== canonical) {\n names.add(entry.displayName)\n }\n }\n return Array.from(names)\n }\n\n const getValues = () => {\n const values: Record<string, FormFieldValue> = {}\n for (const name of getTrackedFieldNames()) {\n values[name] = readFieldValue(getFieldsByName(name))\n }\n return values\n }\n\n const getMergedErrors = () => mergeErrors(state.validationErrors, state.manualErrors)\n\n const syncVisualState = (names?: string[]) => {\n if (!state.element) return\n\n // Resolve every input name to its display name so visual\n // attributes (`css-filled`, `css-error`) land on the visible\n // input, never on the hidden raw mirror. After `format()` runs\n // the canonical name (`phone`) is held by the hidden, which\n // lives at the end of the form, outside any label wrapper —\n // so writing the attribute there is invisible to the project\n // CSS that targets `:has([css-error])` on a parent label. The\n // visual attribute contract is documented in\n // `api/format.ts:resolveDisplayNameForName` and in\n // `docs/css-styling.md`.\n //\n // Errors are still keyed by canonical name (the backend's\n // view of the field); reverse-resolve so the lookup works\n // whether the caller passed canonical, display, or both.\n const sourceNames = names ?? getTrackedFieldNames()\n for (const name of sourceNames) {\n const displayName = resolveDisplayNameForName(state, name)\n const canonical = resolveCanonicalForName(state, name) ?? name\n const fields = getFieldsByName(displayName)\n const hasGroupError = Boolean(\n state.validationGroupErrors[canonical]?.length || state.manualErrors[canonical]?.length\n )\n const elementErrors = state.validationElementErrors.get(canonical)\n fields.forEach(field => {\n const hasError = hasGroupError || Boolean(elementErrors?.get(field)?.length)\n applyBooleanAttribute(field, attributes.filled, isFieldFilled(field))\n applyBooleanAttribute(field, attributes.error, hasError)\n if (hasError) field.setAttribute('aria-invalid', 'true')\n else field.removeAttribute('aria-invalid')\n })\n }\n }\n\n // readFieldValue/writeFieldValue are exported from core/dom for direct\n // callers; referenced here so the linter keeps them as part of the\n // controlled API surface.\n void readFieldValue\n void writeFieldValue\n\n const validateNames = (names?: string[]): ValidationResult => {\n const targetNames = names ?? Object.keys(state.validators)\n const values = getValues()\n const nextValidationErrors = names ? cloneErrors(state.validationErrors) : {}\n const nextGroupErrors = names ? cloneErrors(state.validationGroupErrors) : {}\n const nextElementErrors = names\n ? new Map(state.validationElementErrors)\n : new Map<string, Map<FormFieldElement, string[]>>()\n\n for (const name of targetNames) {\n const rules = state.validators[name]\n if (!rules) {\n delete nextValidationErrors[name]\n delete nextGroupErrors[name]\n nextElementErrors.delete(name)\n continue\n }\n const fields = getFieldsByName(name)\n const displayFields = getFieldsByName(resolveDisplayNameForName(state, name))\n const items = fields.map((field, index) => ({\n value: readFieldValue([field]),\n element: displayFields[index] ?? field\n }))\n const result = validateFieldValueDetailed(name, values[name], rules, values, items)\n const messages = [...result.groupErrors, ...result.itemErrors.flatMap(item => item.messages)]\n if (messages.length > 0) nextValidationErrors[name] = messages\n else delete nextValidationErrors[name]\n\n if (result.groupErrors.length > 0) nextGroupErrors[name] = result.groupErrors\n else delete nextGroupErrors[name]\n\n const perElement = new Map<FormFieldElement, string[]>()\n result.itemErrors.forEach(item => {\n if (item.element) perElement.set(item.element, item.messages)\n })\n if (perElement.size > 0) nextElementErrors.set(name, perElement)\n else nextElementErrors.delete(name)\n }\n\n state.validationErrors = nextValidationErrors\n state.validationGroupErrors = nextGroupErrors\n state.validationElementErrors = nextElementErrors\n syncVisualState(names)\n\n return {\n isValid: Object.keys(getMergedErrors()).length === 0,\n errors: cloneErrors(getMergedErrors())\n }\n }\n\n const getAffectedValidationNames = (name: string): string[] => {\n const affected: string[] = []\n const pending = [name]\n const visited = new Set<string>()\n\n for (let index = 0; index < pending.length; index += 1) {\n const current = pending[index]!\n if (visited.has(current)) continue\n visited.add(current)\n affected.push(current)\n\n for (const dependent of validationDependents.get(current) ?? []) {\n if (!visited.has(dependent)) pending.push(dependent)\n }\n }\n\n return affected\n }\n\n const emitFieldWatchers = (name: string) => {\n if (!state.element || !state.api) return\n const callbacks = state.watchedFields.get(name)\n if (!callbacks || callbacks.size === 0) return\n const value = state.api.getValue(name)\n const fieldElement = state.api.getField(name)\n const snapshot = state.api.getState()\n callbacks.forEach(callback => callback(value, fieldElement, state.element!, snapshot))\n }\n\n const scheduleAutoSubmit = () => {\n if (!state.element || !state.autoSubmitEnabled) return\n if (state.autoSubmitTimer) clearTimeout(state.autoSubmitTimer)\n if (state.autoSubmitDebounce > 0) {\n state.autoSubmitTimer = setTimeout(\n () => submitForm(state.element!),\n state.autoSubmitDebounce\n )\n return\n }\n submitForm(state.element)\n }\n\n const helpers: FormControllerHelpers = {\n notifySubscribers,\n clearFieldCache,\n getFieldsByName,\n getTrackedFieldNames,\n getValues,\n getMergedErrors,\n syncVisualState,\n validateNames,\n emitFieldWatchers,\n scheduleAutoSubmit\n }\n\n // ------- DOM event wiring ---------------------------------------------\n\n const handleDelegatedEvent = (event: Event) => {\n const target = event.target\n if (!(target instanceof Element) || !isFieldElement(target)) return\n const name = target.name\n if (!name) return\n\n clearFieldCache()\n\n // A formatted field is exposed under two names: the canonical\n // (e.g. `phone`, on the hidden raw mirror) and the display\n // (`phone_displayed`, on the visible). Resolve whichever side\n // fired this event to the canonical so the controller's\n // validation + error-clearing path runs once on the field the\n // backend actually reads. The display name is the visible's\n // name — `syncVisualState` targets it so visual attributes\n // (`css-filled`, `css-error`) land on the element the user\n // sees, not on the hidden. Project CSS commonly uses\n // `:has([css-filled])` on a parent label to animate the\n // label; that selector only matches when the visible (inside\n // the label) carries the attribute, never the hidden.\n const canonical = resolveCanonicalForName(state, name) ?? name\n const displayName = resolveDisplayNameForName(state, name)\n\n if (normalizedOptions.clearManualErrorsOnChange) {\n delete state.manualErrors[canonical]\n }\n\n syncVisualState([displayName])\n\n const affectedValidationNames = getAffectedValidationNames(canonical)\n if (\n state.isValidated &&\n affectedValidationNames.some(field => state.validators[field] !== undefined)\n ) {\n validateNames(affectedValidationNames)\n }\n\n emitFieldWatchers(canonical)\n if (canonical !== name) emitFieldWatchers(name)\n notifySubscribers()\n scheduleAutoSubmit()\n }\n\n const handleExternalDelegatedEvent = (event: Event) => {\n const target = event.target\n if (!(target instanceof Element) || !isFieldElement(target)) return\n if (target.form !== state.element || state.element?.contains(target)) return\n handleDelegatedEvent(event)\n }\n\n const handleSubmitEvent = (event: Event) => {\n if (!state.element || state.isDestroyed || !state.api) return\n\n if (normalizedOptions.clearErrorsOnSubmit) state.manualErrors = {}\n\n const validation = state.api.validate()\n const handlers = Array.from(state.submitHandlers)\n const shouldPrevent = validation.isValid\n ? handlers.some(handler => handler.preventDefault)\n : true\n\n if (shouldPrevent) event.preventDefault()\n\n state.submitCount += 1\n notifySubscribers()\n\n if (!validation.isValid) {\n for (const name of Object.keys(validation.errors)) {\n const displayName = resolveDisplayNameForName(state, name)\n const fields = getFieldsByName(displayName)\n const elementErrors = state.validationElementErrors.get(name)\n const field =\n fields.find(candidate => {\n if (!candidate.isConnected || candidate.disabled || candidate.hidden) return false\n if (candidate instanceof HTMLInputElement && candidate.type === 'hidden') return false\n return elementErrors?.has(candidate)\n }) ??\n fields.find(candidate => {\n if (!candidate.isConnected || candidate.disabled || candidate.hidden) return false\n return !(candidate instanceof HTMLInputElement && candidate.type === 'hidden')\n })\n if (field) {\n field.focus()\n break\n }\n }\n return\n }\n\n const candidate = event as Event & { submitter?: unknown }\n const submitter = candidate.submitter instanceof HTMLElement\n ? candidate.submitter\n : null\n const { data, formData } = parseFormData(state.element, submitter)\n const snapshot = state.api.getState()\n const pending: Promise<void>[] = []\n let synchronousError: unknown\n let hasSynchronousError = false\n try {\n for (const handler of handlers) {\n const result = handler.callback(state.element!, data, formData, snapshot)\n if (result && typeof result.then === 'function') pending.push(Promise.resolve(result))\n }\n } catch (error) {\n synchronousError = error\n hasSynchronousError = true\n }\n\n if (pending.length > 0) {\n state.pendingSubmitCount += 1\n notifySubscribers()\n void Promise.allSettled(pending).then(() => {\n state.pendingSubmitCount = Math.max(0, state.pendingSubmitCount - 1)\n if (!state.isDestroyed) notifySubscribers()\n })\n }\n\n if (hasSynchronousError) throw synchronousError\n }\n\n const startMutationObserver = () => {\n if (!state.element || typeof MutationObserver === 'undefined') return\n\n state.mutationObserver = new MutationObserver(() => {\n clearFieldCache()\n if (state.isValidated) {\n validateNames()\n } else syncVisualState()\n notifySubscribers()\n })\n\n state.mutationObserver.observe(state.element, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: ['name', 'type']\n })\n }\n\n // ------- Public api composition --------------------------------------\n\n const api: FormController = {\n get element() {\n return state.element\n },\n get f() {\n return state.element\n },\n get options() {\n return state.options\n },\n onSubmit: createOnSubmit(state, helpers),\n addCleanup: createAddCleanup(state, helpers),\n watch: createWatch(state, helpers),\n observe: createObserve(state, helpers),\n unwatch: createUnwatch(state, helpers),\n subscribe: createSubscribe(state, helpers),\n prefill: createPrefill(state, helpers),\n append: createAppend(state, helpers),\n setErrors: createSetErrors(state, helpers),\n clearErrors: createClearErrors(state, helpers),\n setValue: createSetValue(state, helpers),\n getValue: createGetValue(state, helpers),\n getField: createGetField(state, helpers),\n validate: createValidate(state, helpers),\n revalidate: createRevalidate(state, helpers),\n reset: createReset(state, helpers),\n autoSubmit: createAutoSubmit(state, helpers),\n disableAutoSubmit: createDisableAutoSubmit(state, helpers),\n getData: createGetData(state, helpers),\n getState: createGetState(state, helpers),\n format: createFormat(state, helpers),\n formatAll: createFormatAll(state, helpers),\n destroy: createDestroy(state, helpers)\n }\n\n state.api = api\n\n // ------- Lifecycle hooks ---------------------------------------------\n\n if (state.element) {\n addListener(state.element, 'input', handleDelegatedEvent)\n addListener(document, 'input', handleExternalDelegatedEvent)\n addListener(state.element, 'submit', handleSubmitEvent)\n startMutationObserver()\n }\n\n if (normalizedOptions.autoSubmit) {\n api.autoSubmit(normalizedOptions.autoSubmit)\n }\n\n if (normalizedOptions.formats) {\n for (const config of Object.values(normalizedOptions.formats)) {\n api.format(config)\n }\n }\n\n if (state.isValidated) validateNames()\n else syncVisualState()\n\n return api\n}\n","// api/form.ts\n// Public entry point: the `form(target, options)` factory.\n// Thin wrapper around the core orchestrator for ergonomic imports.\n\nimport { createFormController } from '../core/controller'\nimport type { FormControllerOptions, FormTarget } from '../core/types'\n\nexport const form = (\n target: FormTarget,\n options: FormControllerOptions = {}\n) => createFormController(target, options)\n","// Vanilla mirror of the browser IIFE registry helpers.\n// Module-level singleton: the same shape as `window.Forms`, but\n// importable from any bundler. Consumers can spread it into their own\n// globals (`{ ...browser, regex }`) or use it directly.\n//\n// The IIFE bundle (`./global.ts`) consumes this same object — single\n// source of truth for the registry helpers.\n\nimport { form } from '../api/form'\nimport type {\n FormController,\n FormControllerOptions,\n FormTarget\n} from '../core/types'\n\nexport interface NewFormInput {\n id: string\n options?: FormControllerOptions\n}\n\nexport interface FormsAvailable {\n [id: string]: FormController\n}\n\nexport interface FormsApi {\n form: (\n target: FormTarget,\n options?: FormControllerOptions\n ) => FormController\n newForm: (input: NewFormInput) => FormController | undefined\n destroyForm: (id: string) => void\n available: FormsAvailable\n}\n\nconst available: FormsAvailable = {}\n\nconst newForm = (input: NewFormInput): FormController | undefined => {\n const { id, options } = input\n if (!id) {\n console.error('Form ID is required')\n return\n }\n available[id]?.destroy()\n const controller = form(id, { ...options })\n available[id] = controller\n return controller\n}\n\nconst destroyForm = (id: string): void => {\n if (!id) {\n console.error('Form ID is required')\n return\n }\n const controller = available[id]\n if (controller) {\n controller.destroy()\n delete available[id]\n } else {\n console.warn(`Form with ID ${id} not found`)\n }\n}\n\nexport const browser: FormsApi = {\n form,\n newForm,\n destroyForm,\n available\n}\n","// Browser entrypoint. Exposes a minimal surface as a single global\n// accessible via `window.Forms` (or `globalThis.Forms`).\n//\n// Public shape:\n// - Forms.form(target, options)\n// - Forms.newForm({ id, options })\n// - Forms.destroyForm(id)\n// - Forms.available (registry keyed by form id)\n//\n// The IIFE bundle consumes the same `browser` singleton as the vanilla\n// entrypoint — single source of truth for the registry helpers.\n\nimport { browser } from './registry'\nimport type { FormsApi } from './registry'\n\nconst Forms: FormsApi = browser\n\nexport const form = Forms.form\nexport const newForm = Forms.newForm\nexport const destroyForm = Forms.destroyForm\nexport const available = Forms.available\n\ndeclare global {\n interface Window {\n Forms: FormsApi\n }\n}\n\nif (typeof globalThis !== 'undefined') {\n ;(globalThis as typeof globalThis & { Forms: FormsApi }).Forms = Forms\n}\n\nexport default Forms\n"],"mappings":"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","names":["available","destroyForm","form","newForm","state","state","state","state","form","newCaret","state","registry","state","state","state","state","form","state","state","state","state","state","state","state","state","state","state","state","state","addListener","target","candidate","form","newForm","destroyForm","available"]}
1
+ {"version":3,"sources":["../../src/browser/index.ts","../../src/api/append.ts","../../src/api/add-cleanup.ts","../../src/api/auto-submit.ts","../../src/api/clear-errors.ts","../../src/core/format-helpers.ts","../../src/core/formatter-loader.ts","../../src/api/format.ts","../../src/api/destroy.ts","../../src/api/disable-auto-submit.ts","../../src/core/serialize.ts","../../src/api/get-data.ts","../../src/api/get-field.ts","../../src/core/state.ts","../../src/api/get-state.ts","../../src/core/dom.ts","../../src/api/get-value.ts","../../src/api/observe.ts","../../src/api/on-submit.ts","../../src/api/prefill.ts","../../src/api/reset.ts","../../src/api/validate.ts","../../src/api/revalidate.ts","../../src/api/set-errors.ts","../../src/api/set-value.ts","../../src/api/subscribe.ts","../../src/api/unwatch.ts","../../src/api/watch.ts","../../src/core/validation.ts","../../src/core/controller.ts","../../src/api/form.ts","../../src/browser/registry.ts","../../src/browser/global.ts"],"sourcesContent":["export {\n available,\n default,\n destroyForm,\n form,\n newForm\n} from './global'\nexport type { FormsApi, FormsAvailable, NewFormInput } from './registry'\n","// api/append.ts\n// Appends DOM content to the bound form. If a class is provided, removes\n// any existing node with that class first to avoid duplicates.\n// Returns the created node or null when no element is bound.\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\nimport type { AppendContentOptions } from '../core/types'\n\nexport const createAppend =\n (state: FormControllerState, helpers: FormControllerHelpers) =>\n ({ tag, content, class: className, atStart = false }: AppendContentOptions) => {\n if (!state.element || state.isDestroyed) return null\n\n helpers.clearFieldCache()\n\n if (className) {\n const classSelector = className.trim().split(/\\s+/)[0]\n const existing = classSelector\n ? state.element.getElementsByClassName(classSelector)[0]\n : null\n existing?.remove()\n }\n\n const node = document.createElement(tag)\n if (className) node.className = className\n node.innerHTML = content\n\n if (atStart && state.element.firstChild) {\n state.element.insertBefore(node, state.element.firstChild)\n } else {\n state.element.appendChild(node)\n }\n\n return node\n }\n","// api/add-cleanup.ts\n// Registers controller-owned teardown work and returns an unregister function.\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\nimport type { FormCleanup } from '../core/types'\n\nexport const createAddCleanup =\n (state: FormControllerState, _helpers: FormControllerHelpers) =>\n (cleanup: FormCleanup): (() => void) => {\n if (state.isDestroyed) {\n cleanup()\n return () => undefined\n }\n\n const registration = () => cleanup()\n state.cleanups.add(registration)\n let active = true\n return () => {\n if (!active) return\n active = false\n state.cleanups.delete(registration)\n }\n }\n","// api/auto-submit.ts\n// Enables native auto-submit. Pass `true` to enable, `false` to disable,\n// or an { debounce } object to delay.\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\n\nexport const createAutoSubmit =\n (state: FormControllerState, helpers: FormControllerHelpers) =>\n (next: boolean | { debounce?: number } = true) => {\n if (state.isDestroyed) return state.api!\n state.autoSubmitEnabled = next !== false\n if (!state.autoSubmitEnabled && state.autoSubmitTimer) {\n clearTimeout(state.autoSubmitTimer)\n state.autoSubmitTimer = null\n }\n state.autoSubmitDebounce =\n typeof next === 'object' && next ? next.debounce ?? 0 : 0\n helpers.notifySubscribers()\n return state.api!\n }\n","// api/clear-errors.ts\n// Clears manual errors. With no args, clears every manual error. With a\n// list, clears only those fields.\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\n\nexport const createClearErrors =\n (state: FormControllerState, helpers: FormControllerHelpers) =>\n (fields?: string[]) => {\n if (state.isDestroyed) return state.api!\n if (!fields) {\n state.manualErrors = {}\n helpers.syncVisualState()\n helpers.notifySubscribers()\n return state.api!\n }\n\n for (const field of fields) {\n delete state.manualErrors[field]\n }\n helpers.syncVisualState(fields)\n helpers.notifySubscribers()\n return state.api!\n }\n","// Low-level helpers used by `api/format.ts`:\n// - DOM cursor tracking equivalent to cleave.js' `getNextCursorPosition`\n// (so typing/backspacing inside formatted inputs keeps the caret\n// in a sensible place, even when delimiters are inserted or removed).\n// - `postDelimiterBackspace` contract: after a deletion, the caret\n// is pulled backwards past any leading delimiter so the user never\n// ends up parked with a format character on the caret's left.\n// - Hidden-mirror lookup & creation: each formatted input gets a\n// hidden sibling (`type=\"hidden\"`) that holds the backend-ready\n// `raw` value under the **canonical** field name (the one the\n// developer wrote in the HTML and the one the backend reads).\n// The visible input is renamed to `<field>_displayed` on the\n// first `format()` call. Both names are first-class in the\n// controller's API — see `docs/api/format.md` for the contract.\n//\n// The helpers here are pure DOM utilities — they do not know about the\n// controller state.\n\nimport type { FormFieldElement } from './types'\n\n/**\n * Marks a hidden input as the raw mirror of a formatted field.\n * Presence of this attribute is how `format()` distinguishes\n * mirrors it owns (created at `format()` time) from mirrors the\n * developer pre-authored in the HTML (which survive `destroy()`).\n */\nexport const FORMATTER_RAW_ATTRIBUTE = 'data-formatter-raw-for'\n\n// Resolve a field by name. Returns the first matching element or `null`.\nexport const getFormatterField = (\n form: HTMLFormElement,\n name: string\n): HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | null => {\n const selector = `[name=\"${cssEscape(name)}\"]`\n const node = form.querySelector(selector)\n if (\n node instanceof HTMLInputElement ||\n node instanceof HTMLTextAreaElement ||\n node instanceof HTMLSelectElement\n ) {\n return node\n }\n return null\n}\n\n// Minimal CSS.escape polyfill for selectors that may contain special\n// characters (`[1].phone`, `email[primary]`, etc.).\nconst cssEscape = (value: string): string => {\n if (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') {\n return CSS.escape(value)\n }\n return value.replace(/([!\"#$%&'()*+,./:;<=>?@\\[\\\\\\]^`{|}~])/g, '\\\\$1')\n}\n\n// Find the visible (non-hidden) input that owns the formatted pair for\n// `fieldName`. Looks up by the display name (defaults to\n// `<fieldName>_displayed`, or whatever the developer passed in\n// `FieldFormatConfig.displayField`). Returns `null` when no visible\n// field matches.\nexport const findVisibleField = (\n form: HTMLFormElement,\n displayName: string\n): HTMLInputElement | HTMLTextAreaElement | null => {\n const node = form.querySelector(\n `[name=\"${cssEscape(displayName)}\"]:not([type=\"hidden\"])`\n )\n if (node instanceof HTMLInputElement || node instanceof HTMLTextAreaElement) {\n return node\n }\n return null\n}\n\n// Find the hidden input that mirrors `raw` for the given canonical\n// field name. Returns the existing element or `null`.\n//\n// The canonical name is the one the developer wrote in the HTML\n// (e.g. `phone`) and the one the backend reads. The hidden input\n// is named `phone` and carries the raw value. It is identified\n// either by the `data-formatter-raw-for` attribute (set when the\n// controller creates the mirror) or by a pre-authored hidden with\n// the same name (no attribute — owned externally, preserved on\n// `destroy()`).\nexport const findHiddenMirror = (\n form: HTMLFormElement,\n fieldName: string\n): HTMLInputElement | null => {\n const byAttr = form.querySelector<HTMLInputElement>(\n `[${FORMATTER_RAW_ATTRIBUTE}=\"${cssEscape(fieldName)}\"]`\n )\n if (byAttr) return byAttr\n\n const byName = form.querySelector<HTMLInputElement>(\n `input[type=\"hidden\"][name=\"${cssEscape(fieldName)}\"]`\n )\n if (byName) return byName\n\n return null\n}\n\n// Create a hidden input that will mirror the raw value of `fieldName`.\n// Idempotent: if a mirror already exists it is returned as-is. The\n// mirror is named with the canonical `fieldName` (not a derived\n// suffix) so the value the backend reads sits at the developer's\n// natural name. The `data-formatter-raw-for` attribute is set so\n// `destroy()` can identify the mirror as owned and remove it.\nexport const ensureHiddenMirror = (\n form: HTMLFormElement,\n fieldName: string\n): HTMLInputElement => {\n const existing = findHiddenMirror(form, fieldName)\n if (existing) return existing\n\n const input = document.createElement('input')\n input.type = 'hidden'\n input.name = fieldName\n input.setAttribute('aria-hidden', 'true')\n input.tabIndex = -1\n input.setAttribute(FORMATTER_RAW_ATTRIBUTE, fieldName)\n form.appendChild(input)\n return input\n}\n\n// Rename a visible input from its current `name` to `displayName`.\n// Idempotent: if the input already has `displayName` it is left\n// alone. Used by `format()` to migrate the developer's authored\n// `<input name=\"phone\">` to `<input name=\"phone_displayed\">` so\n// the canonical `name` slot is free for the hidden raw mirror.\nexport const renameVisibleField = (\n visible: HTMLInputElement | HTMLTextAreaElement,\n displayName: string\n): void => {\n if (visible.name === displayName) return\n visible.name = displayName\n}\n\n// Restore a visible input to `originalName`. Called by the format\n// registry on `destroy()` to put the DOM back the way the developer\n// authored it (modulo the value, which the developer controls).\nexport const restoreVisibleName = (\n visible: HTMLInputElement | HTMLTextAreaElement,\n originalName: string\n): void => {\n visible.name = originalName\n}\n\n// Remove every hidden mirror owned by `format()`. Mirrors created by\n// this controller carry the `data-formatter-raw-for` attribute;\n// mirrors that existed before are left untouched.\nexport const removeOwnedHiddenMirrors = (form: HTMLFormElement): void => {\n form\n .querySelectorAll<HTMLInputElement>(`[${FORMATTER_RAW_ATTRIBUTE}]`)\n .forEach(node => node.remove())\n}\n\n// Write `formatted` into the visible field and `raw` into the hidden\n// mirror, preserving the cursor as best as possible.\n//\n// Returns the new caret position so callers can debug or run additional\n// assertions. The function avoids triggering `MutationObserver` cascades\n// for the hidden mirror by setting `.value` directly (the controller's\n// own delegated handler ignores hidden inputs via `isFieldElement`).\n//\n// `inputType` is the optional `InputEvent.inputType` value (e.g.\n// `'insertText'`, `'deleteContentBackward'`, `'deleteWordBackward'`).\n// When it indicates a deletion, this helper mirrors cleave.js'\n// `postDelimiterBackspace` contract: after the caret is re-anchored in\n// the freshly formatted value, walk backwards while the character\n// immediately to the left of the caret is a format character (delimiter,\n// prefix/suffix symbol, …) and decrement the caret. That keeps the\n// caret from \"jumping\" past a delimiter that disappeared when the user\n// deleted the digit sitting right next to it.\nexport const applyFormattedValue = (\n visible: HTMLInputElement | HTMLTextAreaElement,\n mirror: HTMLInputElement | null,\n formatted: string,\n raw: string,\n inputType?: string\n): number => {\n // Capture the caret in the **raw** input the user just typed — this\n // is what cleave's `getNextCursorPosition` does. Using the formatted\n // previous value would lose information when the user edits in the\n // middle of a masked field (deletion adjacent to delimiters, etc.).\n const rawInput = visible.value\n const caret = clamp(\n visible.selectionStart ?? rawInput.length,\n 0,\n rawInput.length\n )\n\n if (mirror && mirror.value !== raw) mirror.value = raw\n\n if (rawInput === formatted) {\n // The formatter did not change the visible value — usually because\n // the user is editing at the tail of a fully-formatted string. Even\n // so, a backspace can leave the caret sitting with a delimiter on\n // its left (because the digit right after the delimiter just\n // disappeared). Apply the postDelimiterBackspace scan in this path\n // too so the contract holds for both branches.\n const newCaret = pullCaretPastLeadingDelimiter(caret, formatted, inputType)\n if (newCaret !== caret) restoreCursor(visible, newCaret)\n return newCaret\n }\n\n visible.value = formatted\n\n const newCaret = pullCaretPastLeadingDelimiter(\n computeCursorPosition(caret, rawInput, formatted),\n formatted,\n inputType\n )\n restoreCursor(visible, newCaret)\n return newCaret\n}\n\n// Mirrors cleave.js' `postDelimiterBackspace` contract. After a\n// deletion (`inputType` starts with `'delete'`) we never want the\n// caret to sit with a format character immediately on its left; if it\n// would, pull it backwards until it does (or until we hit index 0).\n// No-op when `inputType` is missing or does not indicate a deletion.\nconst pullCaretPastLeadingDelimiter = (\n caret: number,\n value: string,\n inputType: string | undefined\n): number => {\n if (!inputType || !inputType.startsWith('delete')) return caret\n\n let cursor = caret\n while (cursor > 0 && isFormatChar(value[cursor - 1]!)) {\n cursor -= 1\n }\n return cursor\n}\n\nconst clamp = (value: number, min: number, max: number): number =>\n Math.max(min, Math.min(max, value))\n\n// `computeCursorPosition` is a cleave-style implementation:\n// - count \"non-format\" characters (digits / letters) between the caret\n// and the start of the **raw input the user just typed**, then\n// locate that same logical index in the new formatted value\n// (jumping over inserted delimiters).\n// - when the caret is at (or past) the end of the raw input, place\n// it at the end of the formatted value.\n//\n// The algorithm intentionally avoids any cleave-zen dependency so this\n// file is pure DOM arithmetic.\nexport const computeCursorPosition = (\n caretPos: number,\n rawInput: string,\n newValue: string\n): number => {\n if (newValue.length === 0) return 0\n\n // Caret at (or past) the end of the raw input — place at new end.\n if (caretPos >= rawInput.length) return newValue.length\n\n // Caret within the raw input: count non-format characters to its\n // LEFT (i.e. how many real characters are before the caret) and\n // find that same count in the new formatted value.\n const targetNonFormat = countNonFormat(rawInput.slice(0, caretPos))\n\n if (targetNonFormat === 0) {\n // Skip leading delimiters / prefix symbols (`$`, `+`, …) so the\n // caret lands at the first real character of the new value.\n let i = 0\n while (i < newValue.length && isFormatChar(newValue[i]!)) i += 1\n return i\n }\n\n let seen = 0\n for (let i = 0; i < newValue.length; i += 1) {\n if (!isFormatChar(newValue[i]!)) {\n seen += 1\n if (seen === targetNonFormat) {\n return Math.min(i + 1, newValue.length)\n }\n }\n }\n\n return newValue.length\n}\n\nconst countNonFormat = (value: string): number => {\n let count = 0\n for (const char of value) {\n if (!isFormatChar(char)) count += 1\n }\n return count\n}\n\n// \"Format characters\" are delimiters, prefix symbols, and other\n// non-alphanumeric separators that the formatter may insert or remove.\n// We deliberately keep this conservative — anything that is not a digit\n// or ASCII letter counts as a format character.\nexport const isFormatChar = (char: string): boolean => {\n if (!char) return false\n if (char >= '0' && char <= '9') return false\n if ((char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z')) return false\n return true\n}\n\nconst restoreCursor = (\n field: HTMLInputElement | HTMLTextAreaElement,\n position: number\n): void => {\n // Moving the selection of a control the user is not editing is never\n // wanted, and Safari focuses an input when its selection is set.\n if (field.ownerDocument.activeElement !== field) return\n\n const max = field.value.length\n const safe = clamp(position, 0, max)\n\n const apply = () => {\n try {\n field.setSelectionRange(safe, safe)\n } catch {\n // Some input types (number, email) throw on setSelectionRange.\n // Ignore — the caret just stays where the browser put it.\n }\n }\n\n // Android keyboard fix: defer one frame so the IME catches up before\n // we re-position the caret. Mirrors cleave.js' `isAndroid` branch.\n if (typeof navigator !== 'undefined' && /Android/i.test(navigator.userAgent)) {\n if (typeof requestAnimationFrame === 'function') {\n requestAnimationFrame(apply)\n } else {\n setTimeout(apply, 1)\n }\n return\n }\n\n apply()\n}\n\n// `getFormatterField` re-exported above is the canonical entry point,\n// but it is intentionally restricted to controller-owned fields. This\n// looser helper accepts a wider set of inputs for test fixtures.\nexport const isFormatterFieldElement = (node: Element): node is FormFieldElement =>\n node instanceof HTMLInputElement ||\n node instanceof HTMLTextAreaElement ||\n node instanceof HTMLSelectElement","// Lazy singleton loader for the optional `@samline/formatter` peer.\n// The formatter is imported dynamically so the package stays usable\n// even when the peer is not installed: callers receive `null` and a\n// single console.error explaining the missing dependency.\n//\n// The first failure is cached so the warning is printed exactly once\n// per process, not on every `format(...)` call.\n\nexport interface FormatterModule {\n format: (\n value: unknown,\n formatType: string,\n options?: Record<string, unknown>\n ) => { formatted: string; raw: string; type: string }\n FORMAT_TYPES?: readonly string[]\n isFormatType?: (value: unknown) => boolean\n regex?: Record<string, { pattern: RegExp; errorMessage: string }>\n}\n\ntype LoaderState =\n | { status: 'pending'; promise: Promise<FormatterModule | null> }\n | { status: 'resolved'; module: FormatterModule | null }\n\nlet state: LoaderState | null = null\n\nconst MISSING_MESSAGE =\n '[samline/forms] The `format()` and `formatAll()` methods require the ' +\n '@samline/formatter package. Install it with: npm i @samline/formatter ' +\n '(or pnpm/bun/yarn equivalent).'\n\nlet warned = false\n\nconst warnOnce = () => {\n if (warned) return\n warned = true\n console.error(MISSING_MESSAGE)\n}\n\nexport const loadFormatter = (): Promise<FormatterModule | null> => {\n if (state && state.status === 'resolved') {\n if (!state.module) warnOnce()\n return Promise.resolve(state.module)\n }\n\n if (state && state.status === 'pending') {\n return state.promise\n }\n\n const promise = (async () => {\n try {\n // Dynamic import keeps the optional peer out of the eager build graph.\n // The consumer's bundler is responsible for resolving the peer at\n // build time — `tsup` treats `@samline/formatter` as `external` so it\n // does not try to pre-bundle it. Do NOT re-add `/* @vite-ignore */`\n // here: in production it leaves the bare specifier as a literal in\n // the bundle, browsers cannot resolve it, and the formatter silently\n // never loads (the catch below masks the failure).\n const mod = (await import('@samline/formatter')) as FormatterModule\n state = { status: 'resolved', module: mod }\n return mod\n } catch {\n warnOnce()\n state = { status: 'resolved', module: null }\n return null\n }\n })()\n\n state = { status: 'pending', promise }\n return promise\n}\n\n// Test-only helper: reset the cached loader so unit tests can swap the\n// dynamic import between \"installed\" and \"missing\" scenarios.\nexport const __resetFormatterLoaderForTests = (): void => {\n state = null\n warned = false\n}\n\n// Test-only helper: inject a formatter implementation directly,\n// bypassing the dynamic import. Pass `null` to simulate the\n// \"peer not installed\" branch.\nexport const __setFormatterModuleForTests = (\n module: FormatterModule | null\n): void => {\n state = { status: 'resolved', module }\n}\n\nexport const __setFormatterPromiseForTests = (\n promise: Promise<FormatterModule | null>\n): void => {\n state = { status: 'pending', promise }\n}\n","import type { FieldFormatConfig, FormController, FormFieldElement } from '../core/types'\nimport {\n applyFormattedValue,\n ensureHiddenMirror,\n findHiddenMirror,\n findVisibleField,\n FORMATTER_RAW_ATTRIBUTE,\n renameVisibleField,\n restoreVisibleName\n} from '../core/format-helpers'\nimport { loadFormatter } from '../core/formatter-loader'\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\n\ntype FormatEntry = {\n canonicalName: string\n displayName: string\n visible: HTMLInputElement | HTMLTextAreaElement\n mirror: HTMLInputElement\n mirrorIsOwned: boolean\n defaultVisible: string | null\n defaultRaw: string | null\n /** Bound after the formatter peer resolves. */\n handler: ((event: Event) => void) | null\n}\n\nconst UNBOUND: ((event: Event) => void) | null = null\n\nconst registry = new WeakMap<FormControllerState, Map<string, FormatEntry>>()\n\nconst getRegistry = (state: FormControllerState): Map<string, FormatEntry> => {\n let bucket = registry.get(state)\n if (!bucket) {\n bucket = new Map<string, FormatEntry>()\n registry.set(state, bucket)\n }\n return bucket\n}\n\nconst resolveFieldNames = (config: FieldFormatConfig): string[] =>\n Array.isArray(config.field) ? config.field : [config.field]\n\nconst addListener = (\n state: FormControllerState,\n target: EventTarget,\n type: string,\n handler: EventListener,\n useCapture?: boolean\n): void => {\n if (useCapture) {\n target.addEventListener(type, handler, { capture: true })\n } else {\n target.addEventListener(type, handler)\n }\n const entry: {\n element: EventTarget\n type: string\n handler: EventListener\n capture?: boolean\n } = { element: target, type, handler }\n if (useCapture !== undefined) entry.capture = useCapture\n state.listeners.push(entry)\n}\n\nconst readInputType = (event: Event): string | undefined => {\n const candidate = event as Partial<InputEvent>\n return typeof candidate.inputType === 'string' ? candidate.inputType : undefined\n}\n\n// Canonical mirrors use auto detection; visible input is always display-order.\nconst buildAutoInterpretedOptions = (\n formatOptions: Record<string, unknown> | undefined\n): Record<string, unknown> => ({\n ...(formatOptions ?? {}),\n interpretInputAs: 'auto'\n})\n\nconst buildDisplayInterpretedOptions = (\n formatOptions: Record<string, unknown> | undefined\n): Record<string, unknown> => ({\n ...(formatOptions ?? {}),\n interpretInputAs: 'display'\n})\n\nconst shouldForceAutoInterpretation = (\n formatOptions: Record<string, unknown> | undefined\n): boolean => formatOptions?.interpretInputAs === undefined\n\nconst buildHandler = (\n visible: HTMLInputElement | HTMLTextAreaElement,\n mirror: HTMLInputElement,\n formatFn: NonNullable<Awaited<ReturnType<typeof loadFormatter>>>['format'],\n formatType: FieldFormatConfig['type'],\n formatOptions: Record<string, unknown> | undefined\n): ((event: Event) => void) => {\n const handler = (event: Event) => {\n if (!visible.isConnected || !mirror.isConnected) return\n\n const isVisible = event.target === visible\n const isMirror = event.target === mirror\n if (!isVisible && !isMirror) return\n\n const rawInput = isMirror ? mirror.value : visible.value\n const inputType = readInputType(event)\n const sourceOptions = shouldForceAutoInterpretation(formatOptions)\n ? isMirror\n ? buildAutoInterpretedOptions(formatOptions)\n : buildDisplayInterpretedOptions(formatOptions)\n : formatOptions\n const { formatted, raw } = formatFn(rawInput, formatType, sourceOptions)\n\n if (!formatted && !raw) {\n visible.value = ''\n if (mirror.value !== '') mirror.value = ''\n return\n }\n\n applyFormattedValue(visible, mirror, formatted, raw, inputType)\n }\n return handler\n}\n\n// Phase one mutates the DOM synchronously; phase two binds only this call's entries.\nconst applyFormat = async (\n state: FormControllerState,\n helpers: FormControllerHelpers,\n config: FieldFormatConfig\n): Promise<void> => {\n if (!state.element || !state.api) return\n if (Array.isArray(config.field) && config.displayField) {\n console.error(\n '[samline/forms] `displayField` can only be used when `field` is a string.'\n )\n return\n }\n\n const formatType = config.type\n const formatOptions = config.options\n const fieldNames = resolveFieldNames(config)\n const bucket = getRegistry(state)\n\n const phase1Entries: FormatEntry[] = []\n\n // Phase 1: establish the visible/canonical pair synchronously.\n for (const fieldName of fieldNames) {\n const displayName = config.displayField ?? `${fieldName}_displayed`\n\n // Accept either a canonical visible field or a pre-authored display field.\n const canonicalFields = helpers.getFieldsByName(fieldName) as FormFieldElement[]\n const candidates = canonicalFields.filter(\n (f): f is HTMLInputElement | HTMLTextAreaElement =>\n (f instanceof HTMLInputElement && f.type !== 'hidden') ||\n f instanceof HTMLTextAreaElement\n )\n let visible: HTMLInputElement | HTMLTextAreaElement | null = candidates[0] ?? null\n if (!visible) {\n visible = findVisibleField(state.element, displayName)\n }\n if (!visible) continue\n\n const existing = bucket.get(fieldName)\n\n let mirror = findHiddenMirror(state.element, fieldName)\n const mirrorIsOwned = !mirror\n if (!mirror) mirror = ensureHiddenMirror(state.element, fieldName)\n\n renameVisibleField(visible, displayName)\n\n if (existing) {\n existing.displayName = displayName\n existing.mirrorIsOwned = mirrorIsOwned\n state.formattedFields.set(fieldName, {\n config,\n displayName,\n mirrorIsOwned\n })\n phase1Entries.push(existing)\n } else {\n const entry: FormatEntry = {\n canonicalName: fieldName,\n displayName,\n visible,\n mirror,\n mirrorIsOwned,\n defaultVisible: null,\n defaultRaw: null,\n handler: UNBOUND\n }\n bucket.set(fieldName, entry)\n state.formattedFields.set(fieldName, {\n config,\n displayName,\n mirrorIsOwned\n })\n phase1Entries.push(entry)\n }\n }\n\n // Phase 2: load the peer, bind listeners, and normalize initial values.\n const formatter = await loadFormatter()\n if (state.isDestroyed) {\n rollbackPhase1(phase1Entries, bucket, state.formattedFields)\n return\n }\n if (!formatter) {\n rollbackPhase1(phase1Entries, bucket, state.formattedFields)\n return\n }\n\n for (const entry of phase1Entries) {\n if (entry.handler !== UNBOUND) {\n // Reformat without duplicating the existing capture listener.\n const current = entry.visible.value\n if (current !== '') {\n const rebindOptions = shouldForceAutoInterpretation(formatOptions)\n ? buildDisplayInterpretedOptions(formatOptions)\n : formatOptions\n const { formatted, raw } = formatter.format(current, formatType, rebindOptions)\n applyFormattedValue(entry.visible, entry.mirror, formatted, raw)\n }\n continue\n }\n\n const handler = buildHandler(\n entry.visible,\n entry.mirror,\n formatter.format,\n formatType,\n formatOptions\n )\n addListener(state, state.element, 'input', handler, true)\n entry.handler = handler\n\n const initial = entry.visible.value\n if (initial !== '') {\n // Initial values may be canonical server data; preserve explicit modes.\n const initialOptions = shouldForceAutoInterpretation(formatOptions)\n ? buildAutoInterpretedOptions(formatOptions)\n : formatOptions\n const { formatted, raw } = formatter.format(initial, formatType, initialOptions)\n applyFormattedValue(entry.visible, entry.mirror, formatted, raw)\n } else if (entry.mirrorIsOwned && entry.mirror.value !== '') {\n entry.mirror.value = ''\n }\n entry.defaultVisible = entry.visible.value\n entry.defaultRaw = entry.mirror.value\n }\n}\n\nexport const resetFormattedFields = (state: FormControllerState): void => {\n const bucket = registry.get(state)\n if (!bucket) return\n for (const entry of bucket.values()) {\n if (entry.defaultVisible !== null) entry.visible.value = entry.defaultVisible\n if (entry.defaultRaw !== null) entry.mirror.value = entry.defaultRaw\n }\n}\n\n// Restore only entries touched by the current call when peer loading fails.\nconst rollbackPhase1 = (\n entries: FormatEntry[],\n bucket: Map<string, FormatEntry>,\n registry: FormControllerState['formattedFields']\n): void => {\n for (const entry of entries) {\n if (entry.visible.isConnected) {\n restoreVisibleName(entry.visible, entry.canonicalName)\n }\n if (entry.mirrorIsOwned && entry.mirror.isConnected) {\n entry.mirror.remove()\n }\n bucket.delete(entry.canonicalName)\n registry.delete(entry.canonicalName)\n }\n}\n\nexport const createFormat =\n (state: FormControllerState, helpers: FormControllerHelpers) =>\n (config: FieldFormatConfig): FormController => {\n if (state.isDestroyed) return state.api!\n if (!state.element) return state.api!\n\n void applyFormat(state, helpers, config)\n\n return state.api!\n }\n\nexport const createFormatAll = createFormat\n\nexport const resolveCanonicalForName = (\n state: FormControllerState,\n name: string\n): string | null => {\n const bucket = registry.get(state)\n if (!bucket) return null\n for (const [canonical, entry] of bucket) {\n if (canonical === name || entry.displayName === name) return canonical\n }\n return null\n}\n\nexport const resolveDisplayNameForName = (\n state: FormControllerState,\n name: string\n): string => {\n const bucket = registry.get(state)\n if (!bucket) return name\n for (const [canonical, entry] of bucket) {\n if (canonical === name) return entry.displayName\n if (entry.displayName === name) return entry.displayName\n }\n return name\n}\n\nexport const cleanupFormatRegistry = (state: FormControllerState): void => {\n if (!state.element) {\n registry.delete(state)\n return\n }\n\n const bucket = registry.get(state)\n if (!bucket) {\n state.element\n .querySelectorAll<HTMLInputElement>(`[${FORMATTER_RAW_ATTRIBUTE}]`)\n .forEach(node => node.remove())\n return\n }\n\n for (const [fieldName, entry] of bucket) {\n if (entry.handler !== UNBOUND) {\n state.element.removeEventListener('input', entry.handler, true)\n }\n if (entry.visible.isConnected) {\n restoreVisibleName(entry.visible, fieldName)\n }\n if (entry.mirrorIsOwned && entry.mirror.isConnected) {\n entry.mirror.remove()\n }\n bucket.delete(fieldName)\n state.formattedFields.delete(fieldName)\n }\n\n // Remove any orphaned mirrors defensively.\n state.element\n .querySelectorAll<HTMLInputElement>(`[${FORMATTER_RAW_ATTRIBUTE}]`)\n .forEach(node => node.remove())\n}\n","// api/destroy.ts\n// Tears down the controller: removes DOM listeners, disconnects the\n// mutation observer, clears caches, and resets internal state.\n// Safe to call multiple times.\n\nimport { cleanupFormatRegistry } from './format'\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\n\nexport const createDestroy =\n (state: FormControllerState, _helpers: FormControllerHelpers) =>\n () => {\n if (state.isDestroyed) return\n\n state.isDestroyed = true\n for (const listener of state.listeners) {\n const options: EventListenerOptions | undefined = listener.capture\n ? { capture: true }\n : undefined\n if (options) {\n listener.element.removeEventListener(listener.type, listener.handler, options)\n } else {\n listener.element.removeEventListener(listener.type, listener.handler)\n }\n }\n state.listeners.length = 0\n\n if (state.autoSubmitTimer) {\n clearTimeout(state.autoSubmitTimer)\n state.autoSubmitTimer = null\n }\n\n state.mutationObserver?.disconnect()\n state.mutationObserver = null\n\n const cleanups = Array.from(state.cleanups).reverse()\n state.cleanups.clear()\n for (const cleanup of cleanups) {\n try {\n cleanup()\n } catch (error) {\n console.error('[forms] cleanup failed', error)\n }\n }\n\n state.watchedFields.clear()\n state.subscribers.clear()\n state.submitHandlers.clear()\n state.fieldCache.clear()\n state.manualErrors = {}\n state.validationErrors = {}\n state.validationGroupErrors = {}\n state.validationElementErrors.clear()\n state.pendingSubmitCount = 0\n\n // Detach format listeners and drop the hidden raw mirrors that\n // `format()` created for this controller. Mirrors that pre-existed\n // in the DOM (i.e. not owned by us) are preserved.\n cleanupFormatRegistry(state)\n }\n","// api/disable-auto-submit.ts\n// Disables autoSubmit and cancels any pending debounce timer.\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\n\nexport const createDisableAutoSubmit =\n (state: FormControllerState, helpers: FormControllerHelpers) =>\n () => {\n state.autoSubmitEnabled = false\n if (state.autoSubmitTimer) {\n clearTimeout(state.autoSubmitTimer)\n state.autoSubmitTimer = null\n }\n helpers.notifySubscribers()\n return state.api!\n }\n","// Pure serialization helpers for HTMLFormElement.\n// No DOM mutation, no controller state — safe to tree-shake into any bundle.\n\nimport type { SerializedFormResult, SerializedFormValue } from './types'\n\nconst isEmptyFile = (value: FormDataEntryValue): value is File =>\n value instanceof File && value.size === 0 && value.name === ''\n\nconst appendValue = (\n data: Record<string, SerializedFormValue>,\n key: string,\n value: FormDataEntryValue\n): void => {\n if (!Object.prototype.hasOwnProperty.call(data, key)) {\n Object.defineProperty(data, key, {\n value,\n writable: true,\n enumerable: true,\n configurable: true\n })\n return\n }\n const current = data[key] as SerializedFormValue\n if (Array.isArray(current)) {\n current.push(value)\n return\n }\n data[key] = [current, value]\n}\n\nexport const parseFormData = (\n formElement: HTMLFormElement,\n submitter?: HTMLElement | null\n): SerializedFormResult => {\n const raw = submitter\n ? new FormData(formElement, submitter)\n : new FormData(formElement)\n const formData = new FormData()\n const data: Record<string, SerializedFormValue> = {}\n\n raw.forEach((value, key) => {\n if (isEmptyFile(value)) return\n formData.append(key, value)\n appendValue(data, key, value)\n })\n\n return { data, formData }\n}\n","// api/get-data.ts\n// Returns the form serialized as both a plain object and a fresh\n// FormData instance. Empty file inputs are filtered out.\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\nimport type { SerializedFormResult } from '../core/types'\nimport { parseFormData } from '../core/serialize'\n\nexport const createGetData =\n (_state: FormControllerState, _helpers: FormControllerHelpers) =>\n (): SerializedFormResult => {\n if (!_state.element) return { data: {}, formData: new FormData() }\n return parseFormData(_state.element)\n }\n","// api/get-field.ts\n// Returns the underlying DOM field(s) for a given name:\n// - null when no field matches\n// - a single element when there's exactly one\n// - an array when there are multiple (radios, checkbox groups)\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\nimport type { FormFieldElement } from '../core/types'\n\nexport const createGetField =\n (state: FormControllerState, helpers: FormControllerHelpers) =>\n (name: string): FormFieldElement | FormFieldElement[] | null => {\n const fields = helpers.getFieldsByName(name)\n if (fields.length === 0) return null\n return fields.length === 1 ? fields[0]! : fields\n }\n","// Shared mutable state for the controller plus the small pure helpers used\n// by both core utilities and the api method factories.\n\nimport type {\n FieldFormatConfig,\n FormController,\n FormControllerOptions,\n FormCleanup,\n FormErrors,\n FormFieldElement,\n FormFieldWatcher,\n FormStateListener,\n FormStateSnapshot,\n FormValues,\n ValidationResult,\n ValidationSchema,\n VisualAttributes\n} from './types'\n\nexport interface FormControllerState {\n element: HTMLFormElement | null\n options: FormControllerOptions\n attributes: Required<VisualAttributes>\n validators: ValidationSchema\n watchedFields: Map<string, Set<FormFieldWatcher>>\n subscribers: Set<FormStateListener>\n submitHandlers: Set<{\n callback: import('./types').FormSubmitHandler\n preventDefault: boolean\n }>\n fieldCache: Map<string, FormFieldElement[]>\n manualErrors: FormErrors\n validationErrors: FormErrors\n validationGroupErrors: FormErrors\n validationElementErrors: Map<string, Map<FormFieldElement, string[]>>\n isValidated: boolean\n autoSubmitEnabled: boolean\n autoSubmitDebounce: number\n submitCount: number\n pendingSubmitCount: number\n autoSubmitTimer: ReturnType<typeof setTimeout> | null\n /** True while an automatic `requestSubmit()` is dispatching `submit`. */\n isAutoSubmitting: boolean\n /** Batches several programmatic writes (`prefill`) into one auto-submit. */\n autoSubmitSuspended: boolean\n /** True while `reset()` runs the native reset it already handles. */\n isResetting: boolean\n isDestroyed: boolean\n listeners: Array<{\n element: EventTarget\n type: string\n handler: EventListenerOrEventListenerObject\n /**\n * `true` when the listener was registered with `capture: true`.\n * Required for matching `removeEventListener` on teardown —\n * the spec treats capture and bubble as distinct registrations.\n */\n capture?: boolean\n }>\n mutationObserver: MutationObserver | null\n cleanups: Set<FormCleanup>\n // Tracks every field that `format()` is currently formatting, so\n // re-bindings are idempotent and `destroy()` can clean up the\n // associated listeners + hidden raw mirror. Keyed by the canonical\n // field name (the one the developer wrote in the HTML and the one\n // the backend reads). `displayName` is the visible input's name\n // (defaults to `${canonical}_displayed`).\n formattedFields: Map<\n string,\n {\n config: FieldFormatConfig\n displayName: string\n mirrorIsOwned: boolean\n }\n >\n // Back-reference to the assembled public controller. Populated by the\n // controller orchestrator after the api method factories are wired.\n api: FormController | null\n}\n\n// Internal helpers bound by the controller orchestrator and consumed by\n// the api method factories. Kept here so api/*.ts only depends on the\n// shape, not on the orchestrator implementation.\nexport interface FormControllerHelpers {\n notifySubscribers: () => void\n clearFieldCache: () => void\n getFieldsByName: (name: string) => FormFieldElement[]\n getTrackedFieldNames: () => string[]\n getValues: () => FormValues\n getMergedErrors: () => FormErrors\n syncVisualState: (names?: string[]) => void\n validateNames: (names?: string[]) => ValidationResult\n emitFieldWatchers: (name: string) => void\n scheduleAutoSubmit: () => void\n applyReset: () => void\n}\n\nexport const createEmptyFormState = (): FormStateSnapshot => ({\n values: {},\n errors: {},\n filledFields: [],\n isValid: true,\n isValidated: false,\n autoSubmit: false,\n isSubmitting: false,\n submitCount: 0\n})\n\nexport const cloneErrors = (errors: FormErrors): FormErrors =>\n Object.fromEntries(\n Object.entries(errors).map(([field, messages]) => [field, [...messages]])\n )\n\nexport const mergeErrors = (left: FormErrors, right: FormErrors): FormErrors => {\n const merged: FormErrors = { ...left }\n for (const [field, messages] of Object.entries(right)) {\n merged[field] = merged[field] ? [...merged[field], ...messages] : [...messages]\n }\n return merged\n}\n","// api/get-state.ts\n// Builds a snapshot of values, errors, filled fields and submission\n// metadata. Pure read — does not mutate controller state.\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\nimport type { FormStateSnapshot } from '../core/types'\nimport { cloneErrors, createEmptyFormState } from '../core/state'\n\nexport const createGetState =\n (state: FormControllerState, helpers: FormControllerHelpers) =>\n (): FormStateSnapshot => {\n const values = helpers.getValues()\n const errors = cloneErrors(helpers.getMergedErrors())\n const filledFields = Object.keys(values).filter(name => {\n const value = values[name]\n if (Array.isArray(value)) return value.length > 0\n if (typeof value === 'string') return value.trim().length > 0\n return value !== undefined\n })\n\n return {\n ...createEmptyFormState(),\n values,\n errors,\n filledFields,\n isValid: Object.keys(errors).length === 0,\n isValidated: state.isValidated,\n autoSubmit: state.autoSubmitEnabled,\n isSubmitting: state.pendingSubmitCount > 0,\n submitCount: state.submitCount\n }\n }\n","// Low-level DOM helpers used by the controller orchestrator and by api\n// methods that need direct DOM access. Pure: no controller state.\n\nimport type { FormFieldElement, FormFieldValue, FormTarget } from './types'\n\nconst isFormFieldElement = (value: Element): value is FormFieldElement =>\n value instanceof HTMLInputElement ||\n value instanceof HTMLSelectElement ||\n value instanceof HTMLTextAreaElement\n\n// Convention: a name ending with \"[]\" signals multi-value inputs (the\n// de facto HTML form pattern used by PHP, Rails, Express, Spring, etc.\n// to collect repeated inputs as an array on the server). The `[]`\n// suffix is the only signal the controller uses; bare names keep the\n// original first-value semantics.\nconst isArraySyntax = (field: FormFieldElement): boolean =>\n typeof field.name === 'string' && field.name.endsWith('[]')\n\nexport const resolveFormElement = (target: FormTarget): HTMLFormElement | null => {\n if (!target) return null\n if (target instanceof HTMLFormElement) return target\n if (typeof target === 'string') {\n const node = document.getElementById(target)\n return node instanceof HTMLFormElement ? node : null\n }\n if ('current' in target) {\n return target.current instanceof HTMLFormElement ? target.current : null\n }\n return null\n}\n\nexport const getNamedFields = (form: HTMLFormElement): FormFieldElement[] =>\n Array.from(form.elements).filter(\n (element): element is FormFieldElement =>\n isFormFieldElement(element) && Boolean(element.name)\n )\n\nexport const queryNamedFields = (\n form: HTMLFormElement,\n name: string\n): FormFieldElement[] => getNamedFields(form).filter(field => field.name === name)\n\nexport const isFieldElement = (value: Element): value is FormFieldElement =>\n isFormFieldElement(value)\n\nexport const isFieldFilled = (field: FormFieldElement): boolean => {\n if (field instanceof HTMLInputElement) {\n if (field.type === 'checkbox' || field.type === 'radio') return field.checked\n if (field.type === 'file') return Boolean(field.files && field.files.length > 0)\n return field.value.trim() !== ''\n }\n return field.value !== ''\n}\n\nexport const applyBooleanAttribute = (\n field: FormFieldElement,\n attribute: string,\n enabled: boolean\n): void => {\n if (enabled) {\n field.setAttribute(attribute, '')\n } else {\n field.removeAttribute(attribute)\n }\n}\n\nexport const readFieldValue = (fields: FormFieldElement[]): FormFieldValue => {\n if (fields.length === 0) return undefined\n const first = fields[0]\n if (!first) return undefined\n\n // Multi-value inputs: name=\"foo[]\". Only treat the group as an array\n // when there is more than one matching input — a single input with a\n // `[]` suffix still returns a plain string, matching the non-array\n // semantics and avoiding a one-element array that callers would\n // have to flatten.\n if (isArraySyntax(first) && fields.length > 1) {\n if (first instanceof HTMLSelectElement || first instanceof HTMLTextAreaElement) {\n return fields.map(f => f.value)\n }\n\n if (first instanceof HTMLInputElement) {\n if (first.type === 'radio') {\n // Radio groups stay single-valued even with the `[]` suffix —\n // they semantically mean \"one of these\". Find the checked one.\n const checked = fields.find(\n (f): f is HTMLInputElement =>\n f instanceof HTMLInputElement && f.checked\n )\n return checked?.value ?? ''\n }\n\n if (first.type === 'checkbox') {\n // Always return an array of checked values (no single-value\n // collapse) so `Array.isArray(value)` validators stay correct.\n return fields\n .filter(\n (f): f is HTMLInputElement =>\n f instanceof HTMLInputElement && f.checked\n )\n .map(f => f.value)\n }\n\n if (first.type === 'file') {\n // Concatenate every selected file across all matching inputs.\n return fields.flatMap(f =>\n f instanceof HTMLInputElement && f.files ? Array.from(f.files) : []\n )\n }\n }\n\n // text / email / password / number / hidden — one string per input.\n return fields.map(f => f.value)\n }\n\n if (first instanceof HTMLSelectElement && first.multiple) {\n return Array.from(first.selectedOptions, option => option.value)\n }\n\n if (first instanceof HTMLSelectElement || first instanceof HTMLTextAreaElement) {\n return first.value\n }\n\n if (first instanceof HTMLInputElement) {\n if (first.type === 'radio') {\n const checked = fields.find(\n (f): f is HTMLInputElement =>\n f instanceof HTMLInputElement && f.checked\n )\n return checked?.value ?? ''\n }\n\n if (first.type === 'checkbox') {\n const values = fields\n .filter(\n (f): f is HTMLInputElement =>\n f instanceof HTMLInputElement && f.checked\n )\n .map(f => f.value)\n return values.length > 1 ? values : (values[0] ?? '')\n }\n\n if (first.type === 'file') {\n return first.files ? Array.from(first.files) : []\n }\n }\n\n return first.value\n}\n\nexport const writeFieldValue = (fields: FormFieldElement[], value: unknown): void => {\n const normalizedArray = Array.isArray(value)\n ? value.map(item => String(item))\n : null\n const normalizedValue = value === undefined || value === null ? '' : String(value)\n\n // For `name=\"foo[]\"` fields receiving an array, distribute one element\n // per input. Surplus inputs (more inputs than array elements) are\n // cleared to ''; surplus array elements (more elements than inputs)\n // are dropped — there's no input to write to. The distribution only\n // applies to text-like inputs; checkbox / radio / file keep their\n // own special semantics.\n const distributeArray =\n fields.length > 0 &&\n fields[0] !== undefined &&\n isArraySyntax(fields[0]) &&\n normalizedArray !== null\n\n for (let index = 0; index < fields.length; index++) {\n const field = fields[index]\n if (!field) continue\n\n if (field instanceof HTMLInputElement) {\n if (field.type === 'checkbox') {\n field.checked = normalizedArray\n ? normalizedArray.includes(field.value)\n : normalizedValue === field.value\n continue\n }\n if (field.type === 'radio') {\n field.checked = field.value === normalizedValue\n continue\n }\n if (field.type === 'file') {\n if (Array.isArray(value) && value.length === 0) field.value = ''\n continue\n }\n if (distributeArray && normalizedArray) {\n field.value = normalizedArray[index] ?? ''\n continue\n }\n field.value = normalizedValue\n continue\n }\n\n if (field instanceof HTMLSelectElement && field.multiple) {\n const selectedValues = normalizedArray ?? [normalizedValue]\n for (const option of field.options) {\n option.selected = selectedValues.includes(option.value)\n }\n continue\n }\n\n if (distributeArray && normalizedArray) {\n field.value = normalizedArray[index] ?? ''\n continue\n }\n\n field.value = normalizedValue\n }\n}\n\nexport const clearAttributes = (form: HTMLFormElement, attributes: string[]): void => {\n for (const attribute of attributes) {\n getNamedFields(form).forEach(field => field.removeAttribute(attribute))\n }\n}\n\nexport const submitForm = (form: HTMLFormElement): void => {\n if (typeof form.requestSubmit === 'function') {\n form.requestSubmit()\n return\n }\n const submitButton = form.querySelector(\n 'button[type=\"submit\"], input[type=\"submit\"]'\n ) as HTMLButtonElement | HTMLInputElement | null\n if (submitButton) {\n submitButton.click()\n return\n }\n form.submit()\n}\n","// api/get-value.ts\n// Returns the current value of a field, with normalization for checkboxes,\n// radios, files, and multi-value inputs.\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\nimport type { FormFieldValue } from '../core/types'\nimport { readFieldValue } from '../core/dom'\n\nexport const createGetValue =\n (state: FormControllerState, helpers: FormControllerHelpers) =>\n (name: string): FormFieldValue => readFieldValue(helpers.getFieldsByName(name))\n","// api/observe.ts\n// Registers a callback that runs on mount and whenever the field changes.\n// Returns an unsubscribe function.\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\nimport type { FormFieldWatcher } from '../core/types'\n\nexport const createObserve =\n (state: FormControllerState, helpers: FormControllerHelpers) =>\n (field: string, callback: FormFieldWatcher) => {\n if (state.isDestroyed) return () => undefined\n const callbacks = state.watchedFields.get(field) ?? new Set<FormFieldWatcher>()\n callbacks.add(callback)\n state.watchedFields.set(field, callbacks)\n\n if (state.element) {\n callback(state.api!.getValue(field), state.api!.getField(field), state.element, state.api!.getState())\n }\n\n return () => {\n const current = state.watchedFields.get(field)\n current?.delete(callback)\n if (current && current.size === 0) {\n state.watchedFields.delete(field)\n }\n }\n }\n","// api/on-submit.ts\n// Registers a submit handler. preventDefault=true intercepts valid submits,\n// which is the right choice for AJAX/fetch flows. Invalid submits are\n// always prevented regardless of the flag.\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\nimport type { FormSubmitHandler } from '../core/types'\n\nexport const createOnSubmit =\n (state: FormControllerState, _helpers: FormControllerHelpers) =>\n (callback: FormSubmitHandler, preventDefault = true) => {\n if (state.isDestroyed) return state.api!\n state.submitHandlers.add({ callback, preventDefault })\n return state.api!\n }\n","// api/prefill.ts\n// Reads window.location.search and writes matching values into the form.\n// Pass a field name to scope prefill to a single field. Repeated keys\n// (`?tags[]=a&tags[]=b`) are written together as one array.\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\n\nexport const createPrefill =\n (state: FormControllerState, helpers: FormControllerHelpers) =>\n (fieldName?: string) => {\n if (!state.element || state.isDestroyed) return state.api!\n\n const params = new URLSearchParams(window.location.search)\n const keys = Array.from(new Set(params.keys())).filter(\n key => !fieldName || fieldName === key\n )\n if (keys.length === 0) return state.api!\n\n // Every written field still runs the delegated pipeline (watchers,\n // validation, visual state); auto-submit waits for the whole batch.\n state.autoSubmitSuspended = true\n try {\n for (const key of keys) {\n const values = params.getAll(key)\n state.api!.setValue(key, values.length > 1 ? values : values[0])\n }\n } finally {\n state.autoSubmitSuspended = false\n }\n helpers.scheduleAutoSubmit()\n return state.api!\n }\n","// api/reset.ts\n// Resets the native form, clears manual and validation errors, strips\n// css-filled/css-error attributes, re-emits field watchers and notifies\n// subscribers. Native reset buttons run the same state pass.\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\n\nexport const createReset =\n (state: FormControllerState, helpers: FormControllerHelpers) =>\n () => {\n if (!state.element || state.isDestroyed) return state.api!\n\n state.isResetting = true\n try {\n state.element.reset()\n } finally {\n state.isResetting = false\n }\n helpers.applyReset()\n return state.api!\n }\n","// api/validate.ts\n// Runs validation. Marks the form as validated, syncs visual state, and\n// returns the validation result for callers that want it.\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\nimport type { ValidationResult } from '../core/types'\n\nexport const createValidate =\n (state: FormControllerState, helpers: FormControllerHelpers) =>\n (fields?: string[]): ValidationResult => {\n // A destroyed controller owns no errors; never repaint the form again.\n if (state.isDestroyed) return { isValid: true, errors: {} }\n state.isValidated = true\n // validateNames synchronizes visual state for the same field set.\n return helpers.validateNames(fields)\n }\n","// api/revalidate.ts\n// Alias of validate(); kept separate for readability at call sites that\n// want to express \"re-run validation now\".\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\nimport { createValidate } from './validate'\n\nexport const createRevalidate = createValidate\n","// api/set-errors.ts\n// Sets manual errors. Accepts an array of field names (each gets a\n// generic message) or a FormErrors map with per-field messages.\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\nimport type { FormErrors } from '../core/types'\nimport { cloneErrors } from '../core/state'\n\nexport const createSetErrors =\n (state: FormControllerState, helpers: FormControllerHelpers) =>\n (fields: string[] | FormErrors) => {\n if (state.isDestroyed) return state.api!\n if (Array.isArray(fields)) {\n state.manualErrors = {\n ...state.manualErrors,\n ...Object.fromEntries(fields.map(field => [field, ['Invalid value.']]))\n }\n } else {\n state.manualErrors = {\n ...state.manualErrors,\n ...cloneErrors(fields)\n }\n }\n\n helpers.syncVisualState(Array.isArray(fields) ? fields : Object.keys(fields))\n helpers.notifySubscribers()\n return state.api!\n }\n","// api/set-value.ts\n// Writes a value into a field and dispatches the correct DOM event so\n// listeners, validators, and visual state run as if a user typed.\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\nimport { writeFieldValue } from '../core/dom'\n\nexport const createSetValue =\n (state: FormControllerState, helpers: FormControllerHelpers) =>\n (name: string, value: unknown) => {\n if (state.isDestroyed) return state.api!\n const fields = helpers.getFieldsByName(name)\n if (fields.length === 0) return state.api!\n\n writeFieldValue(fields, value)\n const firstField = fields[0]\n if (!firstField) return state.api!\n\n firstField.dispatchEvent(new Event('input', { bubbles: true }))\n return state.api!\n }\n","// api/subscribe.ts\n// Subscribes to global form state. Listener fires once immediately with\n// the current snapshot, then on every state mutation. Returns an\n// unsubscribe function.\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\nimport type { FormStateListener } from '../core/types'\n\nexport const createSubscribe =\n (state: FormControllerState, _helpers: FormControllerHelpers) =>\n (listener: FormStateListener) => {\n if (state.isDestroyed) return () => undefined\n state.subscribers.add(listener)\n listener(state.api!.getState())\n return () => {\n state.subscribers.delete(listener)\n }\n }\n","// api/unwatch.ts\n// Removes watched callbacks. With no args, clears every watcher. With only\n// a field, removes all callbacks for that field. With field + callback,\n// removes just that callback.\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\nimport type { FormFieldWatcher } from '../core/types'\n\nexport const createUnwatch =\n (state: FormControllerState, _helpers: FormControllerHelpers) =>\n (field?: string, callback?: FormFieldWatcher) => {\n if (!field) {\n state.watchedFields.clear()\n return state.api!\n }\n\n if (!callback) {\n state.watchedFields.delete(field)\n return state.api!\n }\n\n const callbacks = state.watchedFields.get(field)\n callbacks?.delete(callback)\n if (callbacks && callbacks.size === 0) {\n state.watchedFields.delete(field)\n }\n return state.api!\n }\n","// api/watch.ts\n// Chainable alias over `observe`. Use this when you want a fluent setup\n// without holding the returned unsubscribe function.\n\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\nimport type { FormFieldWatcher } from '../core/types'\nimport { createObserve } from './observe'\n\nexport const createWatch = (state: FormControllerState, helpers: FormControllerHelpers) =>\n (field: string, callback: FormFieldWatcher) => {\n const observe = createObserve(state, helpers)\n observe(field, callback)\n return state.api!\n }\n","// Pure validation routines. No DOM, no controller state.\n\nimport type {\n FieldValidationContext,\n FieldValidationRules,\n FormErrors,\n FormFieldValue,\n FormValues,\n RuleConfig,\n ValueValidationRules,\n ValidationResult,\n ValidationSchema\n} from './types'\n\nconst resolveRule = <T>(rule: RuleConfig<T> | undefined) => {\n if (rule === undefined) {\n return { value: undefined as T | undefined, message: undefined as string | undefined }\n }\n if (typeof rule === 'object' && rule !== null && 'value' in rule) {\n return { value: rule.value, message: rule.message }\n }\n return { value: rule, message: undefined as string | undefined }\n}\n\nconst hasValue = (value: FormFieldValue): boolean => {\n if (Array.isArray(value)) return value.length > 0\n if (typeof value === 'string') return value.trim().length > 0\n return value !== undefined\n}\n\nconst getValueLength = (value: FormFieldValue): number => {\n if (Array.isArray(value)) return value.length\n if (typeof value === 'string') return value.length\n return 0\n}\n\nconst toPatternTarget = (value: FormFieldValue): string => {\n if (typeof value === 'string') return value\n if (Array.isArray(value)) {\n return value\n .map(entry => (typeof entry === 'string' ? entry : entry.name))\n .join(',')\n }\n return ''\n}\n\nconst valuesAreEqual = (left: FormFieldValue, right: FormFieldValue): boolean => {\n if (Array.isArray(left) && Array.isArray(right)) {\n return left.length === right.length && left.every((entry, index) => entry === right[index])\n }\n return left === right\n}\n\nconst DECIMAL_PATTERN = /^[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)$/\n\nconst parseStrictNumber = (value: FormFieldValue): number | undefined => {\n if (typeof value !== 'string') return undefined\n const candidate = value.trim()\n if (!DECIMAL_PATTERN.test(candidate)) return undefined\n const number = Number(candidate)\n return Number.isFinite(number) ? number : undefined\n}\n\nexport interface FieldValidationItem {\n value: FormFieldValue\n element?: import('./types').FormFieldElement\n}\n\nexport interface DetailedValidationResult {\n groupErrors: string[]\n itemErrors: Array<{\n index: number\n element?: import('./types').FormFieldElement\n messages: string[]\n }>\n}\n\nconst validateValue = (\n field: string,\n value: FormFieldValue,\n rules: ValueValidationRules,\n values: FormValues,\n contextOverrides: Partial<Pick<FieldValidationContext, 'element' | 'index'>> = {}\n): string[] => {\n const errors: string[] = []\n const context: FieldValidationContext = {\n field,\n value,\n values,\n ...contextOverrides\n }\n const required = resolveRule(rules.required)\n const minLength = resolveRule(rules.minLength)\n const maxLength = resolveRule(rules.maxLength)\n const pattern = resolveRule(rules.pattern)\n const numeric = resolveRule(rules.numeric)\n const min = resolveRule(rules.min)\n const max = resolveRule(rules.max)\n\n if (required.value && !hasValue(value)) {\n errors.push(required.message ?? 'This field is required.')\n }\n if (minLength.value !== undefined && getValueLength(value) < minLength.value) {\n errors.push(minLength.message ?? `Minimum length is ${minLength.value}.`)\n }\n if (maxLength.value !== undefined && getValueLength(value) > maxLength.value) {\n errors.push(maxLength.message ?? `Maximum length is ${maxLength.value}.`)\n }\n if (pattern.value && hasValue(value)) {\n pattern.value.lastIndex = 0\n const matches = pattern.value.test(toPatternTarget(value))\n pattern.value.lastIndex = 0\n if (!matches) {\n errors.push(pattern.message ?? 'Value does not match the required pattern.')\n }\n }\n\n if (hasValue(value) && (numeric.value || min.value !== undefined || max.value !== undefined)) {\n const number = parseStrictNumber(value)\n if (number === undefined) {\n errors.push(numeric.message ?? 'Value must be a number.')\n } else {\n if (min.value !== undefined && number < min.value) {\n errors.push(min.message ?? `Minimum value is ${min.value}.`)\n }\n if (max.value !== undefined && number > max.value) {\n errors.push(max.message ?? `Maximum value is ${max.value}.`)\n }\n }\n }\n\n const custom = rules.validate\n ? Array.isArray(rules.validate)\n ? rules.validate\n : [rules.validate]\n : []\n\n for (const validator of custom) {\n const result = validator(context)\n if (typeof result === 'string' && result.length > 0) errors.push(result)\n if (result === false) errors.push('Validation failed.')\n }\n\n return errors\n}\n\nexport const validateFieldValueDetailed = (\n field: string,\n value: FormFieldValue,\n rules: FieldValidationRules,\n values: FormValues,\n items?: FieldValidationItem[]\n): DetailedValidationResult => {\n const groupErrors = validateValue(field, value, rules, values)\n const sameAs = resolveRule(rules.sameAs)\n\n if (sameAs.value !== undefined) {\n const otherValue = values[sameAs.value]\n if (hasValue(value) && hasValue(otherValue) && !valuesAreEqual(value, otherValue)) {\n groupErrors.push(sameAs.message ?? `Value must match ${sameAs.value}.`)\n }\n }\n\n const itemErrors: DetailedValidationResult['itemErrors'] = []\n if (rules.each) {\n const sourceItems: FieldValidationItem[] =\n items ??\n (Array.isArray(value)\n ? value.map(entry => ({\n value: typeof entry === 'string' ? entry : ([entry] as File[])\n }))\n : [{ value }])\n\n sourceItems.forEach((item, index) => {\n const overrides: Partial<Pick<FieldValidationContext, 'element' | 'index'>> = {\n index\n }\n if (item.element) overrides.element = item.element\n const messages = validateValue(field, item.value, rules.each!, values, overrides)\n if (messages.length === 0) return\n const error: DetailedValidationResult['itemErrors'][number] = {\n index,\n messages\n }\n if (item.element) error.element = item.element\n itemErrors.push(error)\n })\n }\n\n return { groupErrors, itemErrors }\n}\n\nexport const validateFieldValue = (\n field: string,\n value: FormFieldValue,\n rules: FieldValidationRules,\n values: FormValues\n): string[] => {\n const result = validateFieldValueDetailed(field, value, rules, values)\n return [...result.groupErrors, ...result.itemErrors.flatMap(item => item.messages)]\n}\n\nexport const validateValues = (\n values: FormValues,\n schema: ValidationSchema\n): ValidationResult => {\n const errors: FormErrors = {}\n for (const [field, rules] of Object.entries(schema)) {\n const messages = validateFieldValue(field, values[field], rules, values)\n if (messages.length > 0) errors[field] = messages\n }\n return {\n isValid: Object.keys(errors).length === 0,\n errors\n }\n}\n","// Core orchestrator. Wires shared state, internal helpers and the public\n// api method factories into a single FormController instance.\n//\n// Internal event handlers and DOM wiring live here. Every public method\n// lives in `../api/<method>.ts` as an isolated factory.\n\nimport { createAppend } from '../api/append'\nimport { createAddCleanup } from '../api/add-cleanup'\nimport { createAutoSubmit } from '../api/auto-submit'\nimport { createClearErrors } from '../api/clear-errors'\nimport { createDestroy } from '../api/destroy'\nimport { createDisableAutoSubmit } from '../api/disable-auto-submit'\nimport {\n createFormat,\n createFormatAll,\n resolveCanonicalForName,\n resolveDisplayNameForName,\n resetFormattedFields\n} from '../api/format'\nimport { createGetData } from '../api/get-data'\nimport { createGetField } from '../api/get-field'\nimport { createGetState } from '../api/get-state'\nimport { createGetValue } from '../api/get-value'\nimport { createObserve } from '../api/observe'\nimport { createOnSubmit } from '../api/on-submit'\nimport { createPrefill } from '../api/prefill'\nimport { createReset } from '../api/reset'\nimport { createRevalidate } from '../api/revalidate'\nimport { createSetErrors } from '../api/set-errors'\nimport { createSetValue } from '../api/set-value'\nimport { createSubscribe } from '../api/subscribe'\nimport { createUnwatch } from '../api/unwatch'\nimport { createValidate } from '../api/validate'\nimport { createWatch } from '../api/watch'\nimport { validateFieldValueDetailed } from '../core/validation'\nimport { parseFormData } from './serialize'\nimport {\n applyBooleanAttribute,\n clearAttributes,\n getNamedFields,\n isFieldElement,\n isFieldFilled,\n queryNamedFields,\n readFieldValue,\n resolveFormElement,\n submitForm,\n writeFieldValue\n} from './dom'\nimport { cloneErrors, mergeErrors } from './state'\nimport type {\n FormController,\n FormControllerOptions,\n FormFieldElement,\n FormFieldValue,\n FormTarget,\n ValidationResult,\n VisualAttributes\n} from './types'\nimport type { FormControllerHelpers, FormControllerState } from './state'\n\nconst DEFAULT_ATTRIBUTES: Required<VisualAttributes> = {\n filled: 'css-filled',\n error: 'css-error'\n}\n\nconst DEFAULT_OPTIONS = {\n autoValidate: true,\n clearErrorsOnSubmit: true,\n clearManualErrorsOnChange: true\n} satisfies Pick<\n FormControllerOptions,\n 'autoValidate' | 'clearErrorsOnSubmit' | 'clearManualErrorsOnChange'\n>\n\nexport const createFormController = (\n target: FormTarget,\n options: FormControllerOptions = {}\n): FormController => {\n const element = resolveFormElement(target)\n const attributes = { ...DEFAULT_ATTRIBUTES, ...options.attributes }\n const normalizedOptions: FormControllerOptions = {\n ...DEFAULT_OPTIONS,\n ...options,\n attributes\n }\n\n const state: FormControllerState = {\n element,\n options: normalizedOptions,\n attributes,\n validators: normalizedOptions.validators ?? {},\n watchedFields: new Map(),\n subscribers: new Set(),\n submitHandlers: new Set(),\n fieldCache: new Map(),\n manualErrors: {},\n validationErrors: {},\n validationGroupErrors: {},\n validationElementErrors: new Map(),\n isValidated: Boolean(normalizedOptions.autoValidate),\n autoSubmitEnabled: false,\n autoSubmitDebounce: 0,\n submitCount: 0,\n pendingSubmitCount: 0,\n autoSubmitTimer: null,\n isAutoSubmitting: false,\n autoSubmitSuspended: false,\n isResetting: false,\n isDestroyed: false,\n listeners: [],\n mutationObserver: null,\n cleanups: new Set(),\n formattedFields: new Map(),\n api: null\n }\n\n // Reverse cross-field references once so source changes can revalidate\n // dependent fields without installing per-field listeners.\n const validationDependents = new Map<string, Set<string>>()\n for (const [field, rules] of Object.entries(state.validators)) {\n const sources = new Set<string>()\n const sameAs = rules.sameAs\n if (sameAs !== undefined) {\n sources.add(typeof sameAs === 'string' ? sameAs : sameAs.value)\n }\n if (typeof rules.dependsOn === 'string') sources.add(rules.dependsOn)\n else rules.dependsOn?.forEach(source => sources.add(source))\n\n for (const source of sources) {\n const dependents = validationDependents.get(source) ?? new Set<string>()\n dependents.add(field)\n validationDependents.set(source, dependents)\n }\n }\n\n // ------- Internal helpers (no DOM wiring of their own) -----------------\n\n const notifySubscribers = () => {\n if (!state.api) return\n const snapshot = state.api.getState()\n state.subscribers.forEach(listener => listener(snapshot))\n }\n\n const addListener = (\n node: EventTarget,\n type: string,\n handler: EventListenerOrEventListenerObject,\n capture?: boolean\n ) => {\n if (capture) {\n node.addEventListener(type, handler, { capture: true })\n } else {\n node.addEventListener(type, handler)\n }\n // `exactOptionalPropertyTypes: true` rejects explicit `undefined`\n // values for optional properties; only attach `capture` when it\n // actually has a value.\n const entry: {\n element: EventTarget\n type: string\n handler: EventListenerOrEventListenerObject\n capture?: boolean\n } = { element: node, type, handler }\n if (capture !== undefined) entry.capture = capture\n state.listeners.push(entry)\n }\n\n const clearFieldCache = () => state.fieldCache.clear()\n\n const getFieldsByName = (name: string): FormFieldElement[] => {\n if (!state.element) return []\n const fields = queryNamedFields(state.element, name)\n state.fieldCache.set(name, fields)\n return fields\n }\n\n // Group every named control in one pass over `form.elements`. Reading\n // all values field-by-field would rescan the collection once per name.\n const indexFieldsByName = (): Map<string, FormFieldElement[]> => {\n const index = new Map<string, FormFieldElement[]>()\n if (!state.element) return index\n for (const field of getNamedFields(state.element)) {\n const bucket = index.get(field.name)\n if (bucket) bucket.push(field)\n else index.set(field.name, [field])\n }\n return index\n }\n\n const getTrackedFieldNames = (index = indexFieldsByName()): string[] => {\n if (!state.element) return Object.keys(state.validators)\n const names = new Set<string>(Object.keys(state.validators))\n for (const name of index.keys()) names.add(name)\n // The formatted registry keeps the canonical name as the bucket\n // key. The display name (e.g. `phone_displayed`) is also a\n // tracked field because the developer can `watch`/`getValue` it\n // and `getData()` exposes it in the serialized payload.\n for (const [canonical, entry] of state.formattedFields) {\n names.add(canonical)\n if (entry.displayName && entry.displayName !== canonical) {\n names.add(entry.displayName)\n }\n }\n return Array.from(names)\n }\n\n const getValues = () => {\n const values: Record<string, FormFieldValue> = {}\n const index = indexFieldsByName()\n for (const name of getTrackedFieldNames(index)) {\n values[name] = readFieldValue(index.get(name) ?? [])\n }\n return values\n }\n\n const getMergedErrors = () => mergeErrors(state.validationErrors, state.manualErrors)\n\n const syncVisualState = (names?: string[]) => {\n if (!state.element) return\n\n // Resolve every input name to its display name so visual\n // attributes (`css-filled`, `css-error`) land on the visible\n // input, never on the hidden raw mirror. After `format()` runs\n // the canonical name (`phone`) is held by the hidden, which\n // lives at the end of the form, outside any label wrapper —\n // so writing the attribute there is invisible to the project\n // CSS that targets `:has([css-error])` on a parent label. The\n // visual attribute contract is documented in\n // `api/format.ts:resolveDisplayNameForName` and in\n // `docs/css-styling.md`.\n //\n // Errors are still keyed by canonical name (the backend's\n // view of the field); reverse-resolve so the lookup works\n // whether the caller passed canonical, display, or both.\n const sourceNames = names ?? getTrackedFieldNames()\n for (const name of sourceNames) {\n const displayName = resolveDisplayNameForName(state, name)\n const canonical = resolveCanonicalForName(state, name) ?? name\n const fields = getFieldsByName(displayName)\n const hasGroupError = Boolean(\n state.validationGroupErrors[canonical]?.length || state.manualErrors[canonical]?.length\n )\n const elementErrors = state.validationElementErrors.get(canonical)\n fields.forEach(field => {\n const hasError = hasGroupError || Boolean(elementErrors?.get(field)?.length)\n applyBooleanAttribute(field, attributes.filled, isFieldFilled(field))\n applyBooleanAttribute(field, attributes.error, hasError)\n if (hasError) field.setAttribute('aria-invalid', 'true')\n else field.removeAttribute('aria-invalid')\n })\n }\n }\n\n // readFieldValue/writeFieldValue are exported from core/dom for direct\n // callers; referenced here so the linter keeps them as part of the\n // controlled API surface.\n void readFieldValue\n void writeFieldValue\n\n const validateNames = (names?: string[]): ValidationResult => {\n const targetNames = names ?? Object.keys(state.validators)\n const values = getValues()\n const nextValidationErrors = names ? cloneErrors(state.validationErrors) : {}\n const nextGroupErrors = names ? cloneErrors(state.validationGroupErrors) : {}\n const nextElementErrors = names\n ? new Map(state.validationElementErrors)\n : new Map<string, Map<FormFieldElement, string[]>>()\n\n for (const name of targetNames) {\n const rules = state.validators[name]\n if (!rules) {\n delete nextValidationErrors[name]\n delete nextGroupErrors[name]\n nextElementErrors.delete(name)\n continue\n }\n const fields = getFieldsByName(name)\n const displayFields = getFieldsByName(resolveDisplayNameForName(state, name))\n const items = fields.map((field, index) => ({\n value: readFieldValue([field]),\n element: displayFields[index] ?? field\n }))\n const result = validateFieldValueDetailed(name, values[name], rules, values, items)\n const messages = [...result.groupErrors, ...result.itemErrors.flatMap(item => item.messages)]\n if (messages.length > 0) nextValidationErrors[name] = messages\n else delete nextValidationErrors[name]\n\n if (result.groupErrors.length > 0) nextGroupErrors[name] = result.groupErrors\n else delete nextGroupErrors[name]\n\n const perElement = new Map<FormFieldElement, string[]>()\n result.itemErrors.forEach(item => {\n if (item.element) perElement.set(item.element, item.messages)\n })\n if (perElement.size > 0) nextElementErrors.set(name, perElement)\n else nextElementErrors.delete(name)\n }\n\n state.validationErrors = nextValidationErrors\n state.validationGroupErrors = nextGroupErrors\n state.validationElementErrors = nextElementErrors\n syncVisualState(names)\n\n return {\n isValid: Object.keys(getMergedErrors()).length === 0,\n errors: cloneErrors(getMergedErrors())\n }\n }\n\n const getAffectedValidationNames = (name: string): string[] => {\n const affected: string[] = []\n const pending = [name]\n const visited = new Set<string>()\n\n for (let index = 0; index < pending.length; index += 1) {\n const current = pending[index]!\n if (visited.has(current)) continue\n visited.add(current)\n affected.push(current)\n\n for (const dependent of validationDependents.get(current) ?? []) {\n if (!visited.has(dependent)) pending.push(dependent)\n }\n }\n\n return affected\n }\n\n const emitFieldWatchers = (name: string) => {\n if (!state.element || !state.api) return\n const callbacks = state.watchedFields.get(name)\n if (!callbacks || callbacks.size === 0) return\n const value = state.api.getValue(name)\n const fieldElement = state.api.getField(name)\n const snapshot = state.api.getState()\n callbacks.forEach(callback => callback(value, fieldElement, state.element!, snapshot))\n }\n\n // `requestSubmit()` dispatches `submit` synchronously, so the flag tells\n // the submit handler that no user asked to submit. An invalid automatic\n // submission must not move focus away from the field being edited.\n const runAutoSubmit = () => {\n state.autoSubmitTimer = null\n if (!state.element || state.isDestroyed || !state.autoSubmitEnabled) return\n state.isAutoSubmitting = true\n try {\n submitForm(state.element)\n } finally {\n state.isAutoSubmitting = false\n }\n }\n\n const scheduleAutoSubmit = () => {\n if (!state.element || !state.autoSubmitEnabled || state.autoSubmitSuspended) return\n if (state.autoSubmitTimer) clearTimeout(state.autoSubmitTimer)\n state.autoSubmitTimer = null\n if (state.autoSubmitDebounce > 0) {\n state.autoSubmitTimer = setTimeout(runAutoSubmit, state.autoSubmitDebounce)\n return\n }\n runAutoSubmit()\n }\n\n const emitAllFieldWatchers = () => {\n for (const name of Array.from(state.watchedFields.keys())) emitFieldWatchers(name)\n }\n\n // Shared by `reset()` and native reset buttons: values are already back\n // to their defaults, so clear every error and resynchronize the DOM.\n const applyReset = () => {\n if (!state.element || state.isDestroyed) return\n clearFieldCache()\n resetFormattedFields(state)\n state.manualErrors = {}\n state.validationErrors = {}\n state.validationGroupErrors = {}\n state.validationElementErrors.clear()\n clearAttributes(state.element, [attributes.error, attributes.filled, 'aria-invalid'])\n syncVisualState()\n emitAllFieldWatchers()\n notifySubscribers()\n }\n\n const helpers: FormControllerHelpers = {\n notifySubscribers,\n clearFieldCache,\n getFieldsByName,\n getTrackedFieldNames: () => getTrackedFieldNames(),\n getValues,\n getMergedErrors,\n syncVisualState,\n validateNames,\n emitFieldWatchers,\n scheduleAutoSubmit,\n applyReset\n }\n\n // ------- DOM event wiring ---------------------------------------------\n\n const handleDelegatedEvent = (event: Event) => {\n const target = event.target\n if (!(target instanceof Element) || !isFieldElement(target)) return\n const name = target.name\n if (!name) return\n\n clearFieldCache()\n\n // A formatted field is exposed under two names: the canonical\n // (e.g. `phone`, on the hidden raw mirror) and the display\n // (`phone_displayed`, on the visible). Resolve whichever side\n // fired this event to the canonical so the controller's\n // validation + error-clearing path runs once on the field the\n // backend actually reads. The display name is the visible's\n // name — `syncVisualState` targets it so visual attributes\n // (`css-filled`, `css-error`) land on the element the user\n // sees, not on the hidden. Project CSS commonly uses\n // `:has([css-filled])` on a parent label to animate the\n // label; that selector only matches when the visible (inside\n // the label) carries the attribute, never the hidden.\n const canonical = resolveCanonicalForName(state, name) ?? name\n const displayName = resolveDisplayNameForName(state, name)\n\n if (normalizedOptions.clearManualErrorsOnChange) {\n delete state.manualErrors[canonical]\n }\n\n syncVisualState([displayName])\n\n const affectedValidationNames = getAffectedValidationNames(canonical)\n if (\n state.isValidated &&\n affectedValidationNames.some(field => state.validators[field] !== undefined)\n ) {\n validateNames(affectedValidationNames)\n }\n\n emitFieldWatchers(canonical)\n if (canonical !== name) emitFieldWatchers(name)\n notifySubscribers()\n scheduleAutoSubmit()\n }\n\n const handleExternalDelegatedEvent = (event: Event) => {\n const target = event.target\n if (!(target instanceof Element) || !isFieldElement(target)) return\n if (target.form !== state.element || state.element?.contains(target)) return\n handleDelegatedEvent(event)\n }\n\n const isFocusable = (candidate: FormFieldElement) => {\n if (!candidate.isConnected || candidate.disabled || candidate.hidden) return false\n return !(candidate instanceof HTMLInputElement && candidate.type === 'hidden')\n }\n\n // Focus the invalid control that comes first in the document, not the\n // first rule declared in the schema, so keyboard users land where the\n // form visually starts failing.\n const focusFirstInvalidField = (errors: Record<string, string[]>) => {\n const candidates: FormFieldElement[] = []\n for (const name of Object.keys(errors)) {\n const fields = getFieldsByName(resolveDisplayNameForName(state, name)).filter(isFocusable)\n const elementErrors = state.validationElementErrors.get(name)\n const field = fields.find(candidate => elementErrors?.has(candidate)) ?? fields[0]\n if (field) candidates.push(field)\n }\n candidates.sort((left, right) =>\n left.compareDocumentPosition(right) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1\n )\n candidates[0]?.focus()\n }\n\n const handleSubmitEvent = (event: Event) => {\n if (!state.element || state.isDestroyed || !state.api) return\n\n if (normalizedOptions.clearErrorsOnSubmit) state.manualErrors = {}\n\n const validation = state.api.validate()\n const handlers = Array.from(state.submitHandlers)\n const shouldPrevent = validation.isValid\n ? handlers.some(handler => handler.preventDefault)\n : true\n\n if (shouldPrevent) event.preventDefault()\n\n state.submitCount += 1\n notifySubscribers()\n\n if (!validation.isValid) {\n if (!state.isAutoSubmitting) focusFirstInvalidField(validation.errors)\n return\n }\n\n const candidate = event as Event & { submitter?: unknown }\n const submitter = candidate.submitter instanceof HTMLElement\n ? candidate.submitter\n : null\n const { data, formData } = parseFormData(state.element, submitter)\n const snapshot = state.api.getState()\n const pending: Promise<void>[] = []\n let synchronousError: unknown\n let hasSynchronousError = false\n try {\n for (const handler of handlers) {\n const result = handler.callback(state.element!, data, formData, snapshot)\n if (result && typeof result.then === 'function') {\n pending.push(\n Promise.resolve(result).catch(error => {\n console.error('[forms] submit handler failed', error)\n })\n )\n }\n }\n } catch (error) {\n synchronousError = error\n hasSynchronousError = true\n }\n\n if (pending.length > 0) {\n state.pendingSubmitCount += 1\n notifySubscribers()\n void Promise.allSettled(pending).then(() => {\n state.pendingSubmitCount = Math.max(0, state.pendingSubmitCount - 1)\n if (!state.isDestroyed) notifySubscribers()\n })\n }\n\n if (hasSynchronousError) throw synchronousError\n }\n\n // The `reset` event fires before the browser restores default values,\n // and a user-initiated reset runs microtasks between the two steps, so\n // the state pass waits for the next task. `reset()` handles itself.\n const handleResetEvent = (event: Event) => {\n if (state.isResetting) return\n const timer = setTimeout(() => {\n state.cleanups.delete(cancel)\n if (!event.defaultPrevented) applyReset()\n }, 0)\n const cancel = () => clearTimeout(timer)\n state.cleanups.add(cancel)\n }\n\n const startMutationObserver = () => {\n if (!state.element || typeof MutationObserver === 'undefined') return\n\n const touchesFields = (node: Node) =>\n node instanceof Element &&\n (isFieldElement(node) || node.querySelector('input, select, textarea') !== null)\n\n // Error messages or other markup rendered into the form by a subscriber\n // must not re-enter validation; that would notify the same subscriber\n // again and loop. Only control additions/removals and renames count.\n const isRelevant = (record: MutationRecord) =>\n record.type === 'attributes' ||\n Array.from(record.addedNodes).some(touchesFields) ||\n Array.from(record.removedNodes).some(touchesFields)\n\n state.mutationObserver = new MutationObserver(records => {\n if (state.isDestroyed || !records.some(isRelevant)) return\n clearFieldCache()\n if (state.isValidated) {\n validateNames()\n } else syncVisualState()\n notifySubscribers()\n })\n\n state.mutationObserver.observe(state.element, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: ['name', 'type']\n })\n }\n\n // ------- Public api composition --------------------------------------\n\n const api: FormController = {\n get element() {\n return state.element\n },\n get f() {\n return state.element\n },\n get options() {\n return state.options\n },\n onSubmit: createOnSubmit(state, helpers),\n addCleanup: createAddCleanup(state, helpers),\n watch: createWatch(state, helpers),\n observe: createObserve(state, helpers),\n unwatch: createUnwatch(state, helpers),\n subscribe: createSubscribe(state, helpers),\n prefill: createPrefill(state, helpers),\n append: createAppend(state, helpers),\n setErrors: createSetErrors(state, helpers),\n clearErrors: createClearErrors(state, helpers),\n setValue: createSetValue(state, helpers),\n getValue: createGetValue(state, helpers),\n getField: createGetField(state, helpers),\n validate: createValidate(state, helpers),\n revalidate: createRevalidate(state, helpers),\n reset: createReset(state, helpers),\n autoSubmit: createAutoSubmit(state, helpers),\n disableAutoSubmit: createDisableAutoSubmit(state, helpers),\n getData: createGetData(state, helpers),\n getState: createGetState(state, helpers),\n format: createFormat(state, helpers),\n formatAll: createFormatAll(state, helpers),\n destroy: createDestroy(state, helpers)\n }\n\n state.api = api\n\n // ------- Lifecycle hooks ---------------------------------------------\n\n if (state.element) {\n addListener(state.element, 'input', handleDelegatedEvent)\n addListener(document, 'input', handleExternalDelegatedEvent)\n addListener(state.element, 'submit', handleSubmitEvent)\n addListener(state.element, 'reset', handleResetEvent)\n startMutationObserver()\n }\n\n if (normalizedOptions.autoSubmit) {\n api.autoSubmit(normalizedOptions.autoSubmit)\n }\n\n if (normalizedOptions.formats) {\n for (const config of Object.values(normalizedOptions.formats)) {\n api.format(config)\n }\n }\n\n if (state.isValidated) validateNames()\n else syncVisualState()\n\n return api\n}\n","// api/form.ts\n// Public entry point: the `form(target, options)` factory.\n// Thin wrapper around the core orchestrator for ergonomic imports.\n\nimport { createFormController } from '../core/controller'\nimport type { FormControllerOptions, FormTarget } from '../core/types'\n\nexport const form = (\n target: FormTarget,\n options: FormControllerOptions = {}\n) => createFormController(target, options)\n","// Vanilla mirror of the browser IIFE registry helpers.\n// Module-level singleton: the same shape as `window.Forms`, but\n// importable from any bundler. Consumers can spread it into their own\n// globals (`{ ...browser, regex }`) or use it directly.\n//\n// The IIFE bundle (`./global.ts`) consumes this same object — single\n// source of truth for the registry helpers.\n\nimport { form } from '../api/form'\nimport type {\n FormController,\n FormControllerOptions,\n FormTarget\n} from '../core/types'\n\nexport interface NewFormInput {\n id: string\n options?: FormControllerOptions\n}\n\nexport interface FormsAvailable {\n [id: string]: FormController\n}\n\nexport interface FormsApi {\n form: (\n target: FormTarget,\n options?: FormControllerOptions\n ) => FormController\n newForm: (input: NewFormInput) => FormController | undefined\n destroyForm: (id: string) => void\n available: FormsAvailable\n}\n\nconst available: FormsAvailable = {}\n\nconst newForm = (input: NewFormInput): FormController | undefined => {\n const { id, options } = input\n if (!id) {\n console.error('Form ID is required')\n return\n }\n available[id]?.destroy()\n const controller = form(id, { ...options })\n available[id] = controller\n return controller\n}\n\nconst destroyForm = (id: string): void => {\n if (!id) {\n console.error('Form ID is required')\n return\n }\n const controller = available[id]\n if (controller) {\n controller.destroy()\n delete available[id]\n } else {\n console.warn(`Form with ID ${id} not found`)\n }\n}\n\nexport const browser: FormsApi = {\n form,\n newForm,\n destroyForm,\n available\n}\n","// Browser entrypoint. Exposes a minimal surface as a single global\n// accessible via `window.Forms` (or `globalThis.Forms`).\n//\n// Public shape:\n// - Forms.form(target, options)\n// - Forms.newForm({ id, options })\n// - Forms.destroyForm(id)\n// - Forms.available (registry keyed by form id)\n//\n// The IIFE bundle consumes the same `browser` singleton as the vanilla\n// entrypoint — single source of truth for the registry helpers.\n\nimport { browser } from './registry'\nimport type { FormsApi } from './registry'\n\nconst Forms: FormsApi = browser\n\nexport const form = Forms.form\nexport const newForm = Forms.newForm\nexport const destroyForm = Forms.destroyForm\nexport const available = Forms.available\n\ndeclare global {\n interface Window {\n Forms: FormsApi\n }\n}\n\nif (typeof globalThis !== 'undefined') {\n ;(globalThis as typeof globalThis & { Forms: FormsApi }).Forms = Forms\n}\n\nexport default Forms\n"],"mappings":"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","names":["available","destroyForm","form","newForm","state","state","state","state","form","newCaret","state","registry","state","state","state","state","form","state","state","state","state","state","state","state","state","state","state","state","state","addListener","target","form","newForm","destroyForm","available"]}