@samline/forms 2.2.3 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/api/append.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"],"sourcesContent":["// 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.querySelector(`.${classSelector}`)\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/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 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// - Raw-mirror lookup & creation: each formatted input gets a hidden\n// sibling (`type=\"hidden\"`) that holds the backend-ready `raw`\n// value. The mirror is created on first `format()` call and reused\n// on subsequent calls so re-binding the controller does not duplicate\n// hidden inputs.\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\nexport const FORMATTER_RAW_ATTRIBUTE = 'data-formatter-raw-for'\n\n// Resolve a field by name (used to read the original input that owns the\n// raw mirror). 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 hidden input that mirrors `raw` for the given field name.\n// Returns the existing element or `null`.\nexport const findRawMirror = (\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 // Backwards-compatible lookup: existing deployments may have used\n // `<input name=\"<field>_raw\">` or `<input name=\"<field>Raw\">` to carry the raw value. Reuse it.\n const byName = form.querySelector<HTMLInputElement>(\n `[name=\"${cssEscape(`${fieldName}_raw`)}\"]`\n )\n if (byName) return byName\n const byNameOld = form.querySelector<HTMLInputElement>(\n `[name=\"${cssEscape(`${fieldName}Raw`)}\"]`\n )\n if (byNameOld) return byNameOld\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.\nexport const ensureRawMirror = (\n form: HTMLFormElement,\n fieldName: string\n): HTMLInputElement => {\n const existing = findRawMirror(form, fieldName)\n if (existing) return existing\n\n const input = document.createElement('input')\n input.type = 'hidden'\n input.name = `${fieldName}_raw`\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// Remove every raw mirror owned by `format()`. Mirrors created by this\n// controller carry the `data-formatter-raw-for` attribute; mirrors that\n// existed before are left untouched.\nexport const removeOwnedRawMirrors = (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}","// api/format.ts\n// Applies the optional `@samline/formatter` peer to one or many form\n// fields, with cleave-style cursor tracking and an auto-managed hidden\n// raw mirror.\n//\n// Public surface lives on `FormController` as `format()` and\n// `formatAll()`. Both are chainable and both behave identically — the\n// alias exists only to read naturally when the caller wants to apply\n// the same configuration to several inputs.\n//\n// When `@samline/formatter` is not installed the methods log a single\n// `console.error` (via `loadFormatter`) and return the controller\n// unchanged so the rest of the app keeps working.\n\nimport type { FieldFormatConfig, FormController, FormFieldElement } from '../core/types'\nimport {\n applyFormattedValue,\n ensureRawMirror,\n findRawMirror,\n FORMATTER_RAW_ATTRIBUTE\n} from '../core/format-helpers'\nimport { loadFormatter } from '../core/formatter-loader'\nimport type { FormControllerHelpers, FormControllerState } from '../core/state'\n\n// Listeners added on demand; tracked here so a future `unformat(field)`\n// (out of scope for v1) can detach them without trawling `state.listeners`.\ntype FormatEntry = {\n fields: Set<HTMLInputElement | HTMLTextAreaElement>\n mirrorName: string\n handler: (event: Event) => void\n mirrorIsOwned: boolean\n}\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): void => {\n target.addEventListener(type, handler)\n state.listeners.push({ element: target, type, handler })\n}\n\nconst buildHandler = (\n field: HTMLInputElement | HTMLTextAreaElement,\n mirror: HTMLInputElement | null,\n formatFn: NonNullable<Awaited<ReturnType<typeof loadFormatter>>>['format'],\n formatType: FieldFormatConfig['type'],\n formatOptions: Record<string, unknown> | undefined,\n mirrorFieldName: string\n): ((event: Event) => void) => {\n const handler = (event: Event) => {\n if (!field.isConnected) return\n // Bug #1 fix: ensure the event actually came from this field,\n // since the listener is delegated to the form.\n if (event.target !== field) return\n const rawInput = (event.target as HTMLInputElement | HTMLTextAreaElement).value\n // `InputEvent.inputType` lets the cursor helper distinguish deletions\n // (`deleteContentBackward`, `deleteWordBackward`, …) from insertions\n // and apply cleave's `postDelimiterBackspace` contract: after a\n // deletion the caret must never sit with a delimiter on its left.\n const inputType = readInputType(event)\n const { formatted, raw } = formatFn(rawInput, formatType, formatOptions)\n\n if (!formatted && !raw) {\n field.value = ''\n if (mirror && mirror.value !== '') mirror.value = ''\n return\n }\n\n applyFormattedValue(field, mirror, formatted, raw, inputType)\n // Keep the registry mirror pointer alive for in-place cleanup.\n void mirrorFieldName\n }\n return handler\n}\n\n// `InputEvent.inputType` is the canonical signal for \"what just\n// happened\". The listener is registered as a plain `Event` handler so\n// older / synthetic events without `inputType` simply fall through and\n// behave as insertions — which preserves the pre-existing cursor math.\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// Internal: core apply routine shared by `format()` and `formatAll()`.\n// Returns the controller to preserve chainability even on the missing\n// peer path.\nconst applyFormat = async (\n state: FormControllerState,\n helpers: FormControllerHelpers,\n config: FieldFormatConfig\n): Promise<void> => {\n if (!state.element || !state.api) return\n\n const formatter = await loadFormatter()\n if (!formatter) return\n\n const formatType = config.type\n const formatOptions = config.options\n const fieldNames = resolveFieldNames(config)\n const bucket = getRegistry(state)\n\n for (const fieldName of fieldNames) {\n const fields = helpers.getFieldsByName(fieldName) as FormFieldElement[]\n if (fields.length === 0) continue\n\n // Filter to text-like inputs — the formatter does not work on\n // checkboxes, radios, files, etc.\n const writable = fields.filter(\n (f): f is HTMLInputElement | HTMLTextAreaElement =>\n (f instanceof HTMLInputElement &&\n f.type !== 'checkbox' &&\n f.type !== 'radio' &&\n f.type !== 'file' &&\n f.type !== 'submit' &&\n f.type !== 'button') ||\n f instanceof HTMLTextAreaElement\n )\n\n if (writable.length === 0) continue\n\n for (const field of writable) {\n // Idempotency: a second `format()` call for the same field\n // refreshes the configuration in `state.formattedFields` but\n // does not double-bind listeners or duplicate the hidden mirror.\n const existing = bucket.get(fieldName)\n const mirrorName = config.rawField ?? `${fieldName}_raw`\n // Bug #2a fix: search by fieldName (the data-formatter-raw-for value),\n // not mirrorName (the input name). findRawMirror looks for\n // data-formatter-raw-for=\"<fieldName>\", not data-formatter-raw-for=\"<fieldName>Raw\".\n let mirror = findRawMirror(state.element, fieldName) ?? null\n const mirrorIsOwned = !mirror\n if (!mirror) mirror = ensureRawMirror(state.element, fieldName)\n // Sync the mirror's `name` attribute when the user overrides it\n // via `config.rawField` (default `<field>Raw`).\n if (mirror && mirror.name !== mirrorName) mirror.name = mirrorName\n const trackerMirrorName = mirror?.name ?? mirrorName\n\n if (existing) {\n // Refresh the stored config so `destroy()` knows the entry is\n // still active, but do not re-attach listeners.\n existing.mirrorName = trackerMirrorName\n existing.mirrorIsOwned = mirrorIsOwned\n state.formattedFields.set(fieldName, {\n config,\n mirrorName: trackerMirrorName,\n mirrorIsOwned\n })\n // Bug #2b fix: re-format the current value with the new options\n // (e.g. country_code change needs to re-format the phone).\n // Only re-format when the field has a value; when the field is\n // empty we leave the mirror untouched so pre-existing raw values\n // (set before format() was called) are preserved.\n const current = field.value\n if (current !== '') {\n const { formatted, raw } = formatter.format(current, formatType, formatOptions)\n applyFormattedValue(field, mirror, formatted, raw)\n }\n continue\n }\n\n const handler = buildHandler(\n field,\n mirror,\n formatter.format,\n formatType,\n formatOptions,\n trackerMirrorName\n )\n\n addListener(state, state.element, 'input', handler)\n bucket.set(fieldName, {\n fields: new Set([field]),\n mirrorName: trackerMirrorName,\n handler,\n mirrorIsOwned\n })\n state.formattedFields.set(fieldName, {\n config,\n mirrorName: trackerMirrorName,\n mirrorIsOwned\n })\n\n // Apply the formatter to the current value so pre-filled data\n // (e.g. coming from `setValue`, `prefill`, or server-rendered\n // HTML) is normalised immediately and the hidden mirror is\n // populated without waiting for a user keystroke.\n const initial = field.value\n if (initial !== '') {\n const { formatted, raw } = formatter.format(initial, formatType, formatOptions)\n applyFormattedValue(field, mirror, formatted, raw)\n } else if (mirrorIsOwned && mirror && mirror.value !== '') {\n // Only clear an owned mirror when the field is empty. Pre-existing\n // mirrors are left untouched so their values are preserved.\n mirror.value = ''\n }\n }\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 // Fire and forget: the loader surfaces a single console.error if\n // the peer is missing, then resolves with `null`.\n void applyFormat(state, helpers, config)\n\n return state.api!\n }\n\nexport const createFormatAll = createFormat\n\n// Cleanup hook consumed by `api/destroy.ts`. Removes every listener\n// registered through this module and drops the owned raw mirrors that\n// were created during the controller's lifetime. Mirrors that already\n// existed in the DOM (i.e. `mirrorIsOwned === false`) are left alone.\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 state.element.removeEventListener('input', entry.handler)\n if (entry.mirrorIsOwned) {\n const mirror = findRawMirror(state.element, entry.mirrorName)\n mirror?.remove()\n }\n bucket.delete(fieldName)\n state.formattedFields.delete(fieldName)\n }\n\n // Catch any leftover owned mirrors (defensive — should not happen\n // because every `applyFormat` path registers in the bucket, but the\n // second pass guarantees no orphans escape `destroy()`).\n state.element\n .querySelectorAll<HTMLInputElement>(`[${FORMATTER_RAW_ATTRIBUTE}]`)\n .forEach(node => node.remove())\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 listener.element.removeEventListener(listener.type, listener.handler)\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 state.watchedFields.clear()\n state.subscribers.clear()\n state.submitHandlers.clear()\n state.fieldCache.clear()\n state.manualErrors = {}\n state.validationErrors = {}\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 const current = data[key]\n if (current === undefined) {\n data[key] = value\n return\n }\n if (Array.isArray(current)) {\n current.push(value)\n return\n }\n data[key] = [current, value]\n}\n\nexport const parseFormData = (formElement: HTMLFormElement): SerializedFormResult => {\n const raw = 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 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 isValidated: boolean\n autoSubmitEnabled: boolean\n autoSubmitDebounce: number\n submitCount: number\n autoSubmitTimer: ReturnType<typeof setTimeout> | null\n isDestroyed: boolean\n listeners: Array<{\n element: EventTarget\n type: string\n handler: EventListenerOrEventListenerObject\n }>\n mutationObserver: MutationObserver | null\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 mirrors.\n formattedFields: Map<\n string,\n {\n config: FieldFormatConfig\n mirrorName: 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 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 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\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.querySelectorAll('input[name], select[name], textarea[name]')).filter(\n isFormFieldElement\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 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 (const field of fields) {\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 field.value = normalizedValue\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 form\n .querySelectorAll(`[${attribute}]`)\n .forEach(node => node.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'\n\nexport const createReset =\n (state: FormControllerState, helpers: FormControllerHelpers) =>\n () => {\n if (!state.element) return state.api!\n\n state.element.reset()\n state.manualErrors = {}\n state.validationErrors = {}\n clearAttributes(state.element, [state.attributes.error, state.attributes.filled])\n\n if (state.isValidated) 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 const eventType =\n firstField instanceof HTMLSelectElement ||\n (firstField instanceof HTMLInputElement &&\n (firstField.type === 'checkbox' || firstField.type === 'radio'))\n ? 'change'\n : 'input'\n\n firstField.dispatchEvent(new Event(eventType, { 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 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\nexport const validateFieldValue = (\n field: string,\n value: FormFieldValue,\n rules: FieldValidationRules,\n values: FormValues\n): string[] => {\n const errors: string[] = []\n const context: FieldValidationContext = { field, value, values }\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\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) && !pattern.value.test(toPatternTarget(value))) {\n errors.push(pattern.message ?? 'Value does not match the required pattern.')\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 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 { createAutoSubmit } from '../api/auto-submit'\nimport { createClearErrors } from '../api/clear-errors'\nimport { createDestroy } from '../api/destroy'\nimport { createDisableAutoSubmit } from '../api/disable-auto-submit'\nimport { createFormat, createFormatAll } 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 { validateFieldValue } from '../core/validation'\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 isValidated: Boolean(normalizedOptions.autoValidate),\n autoSubmitEnabled: false,\n autoSubmitDebounce: 0,\n submitCount: 0,\n autoSubmitTimer: null,\n isDestroyed: false,\n listeners: [],\n mutationObserver: null,\n formattedFields: new Map(),\n api: null\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 ) => {\n node.addEventListener(type, handler)\n state.listeners.push({ element: node, type, handler })\n }\n\n const clearFieldCache = () => state.fieldCache.clear()\n\n const getFieldsByName = (name: string): FormFieldElement[] => {\n if (!state.element) return []\n if (!state.fieldCache.has(name)) {\n state.fieldCache.set(name, queryNamedFields(state.element, name))\n }\n return state.fieldCache.get(name) ?? []\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 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 const targetNames = names ?? getTrackedFieldNames()\n const errors = getMergedErrors()\n for (const name of targetNames) {\n const fields = getFieldsByName(name)\n const hasError = Boolean(errors[name]?.length)\n for (const field of fields) {\n applyBooleanAttribute(field, attributes.filled, isFieldFilled(field))\n applyBooleanAttribute(field, attributes.error, hasError)\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\n for (const name of targetNames) {\n const rules = state.validators[name]\n if (!rules) {\n delete nextValidationErrors[name]\n continue\n }\n const messages = validateFieldValue(name, values[name], rules, values)\n if (messages.length > 0) nextValidationErrors[name] = messages\n else delete nextValidationErrors[name]\n }\n\n state.validationErrors = nextValidationErrors\n syncVisualState(names)\n\n return {\n isValid: Object.keys(getMergedErrors()).length === 0,\n errors: cloneErrors(getMergedErrors())\n }\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 if (normalizedOptions.clearManualErrorsOnChange) {\n delete state.manualErrors[name]\n }\n\n syncVisualState([name])\n\n if (state.isValidated && state.validators[name]) {\n validateNames([name])\n }\n\n emitFieldWatchers(name)\n notifySubscribers()\n scheduleAutoSubmit()\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) return\n\n const { data, formData } = state.api.getData()\n const snapshot = state.api.getState()\n handlers.forEach(handler =>\n handler.callback(state.element!, data, formData, snapshot)\n )\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 syncVisualState()\n validateNames()\n }\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 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(state.element, 'change', handleDelegatedEvent)\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) {\n syncVisualState()\n validateNames()\n }\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 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}"],"mappings":"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","names":["state","state","state","form","newCaret","state","state","state","state","state","form","state","state","state","state","state","state","state","state","state","state","state","state","addListener","target"]}
1
+ {"version":3,"sources":["../src/api/append.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"],"sourcesContent":["// 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.querySelector(`.${classSelector}`)\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/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 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}","// api/format.ts\n// Applies the optional `@samline/formatter` peer to one or many form\n// fields, with cleave-style cursor tracking and an auto-managed hidden\n// raw mirror.\n//\n// Mirror convention\n// -----------------\n// `format()` mirrors the developer's authoring intent. The developer\n// writes a single visible input with the name they want the backend\n// to read (the \"canonical\" name, e.g. `phone`). The first time\n// `format()` runs for that field it:\n//\n// 1. Renames the visible from `phone` to `phone_displayed`\n// (configurable via `FieldFormatConfig.displayField`). The\n// visible keeps showing the formatted value the user types.\n// 2. Creates a hidden `<input type=\"hidden\" name=\"phone\">` that\n// carries the raw value the backend ultimately receives.\n//\n// After the rename both names are first-class in the controller's\n// API:\n// - `getValue('phone')` -> raw\n// - `getValue('phone_displayed')` -> formatted\n// - `watch('phone', cb)` -> cb receives the raw value\n// - `watch('phone_displayed', cb)` -> cb receives the formatted value\n// - `setValue('phone', x)` -> writes raw, reformats visible\n// - `setValue('phone_displayed', x)` -> writes formatted, reformats raw\n// - `getData()` -> FormData carries both keys\n// - `validators.phone` -> runs against the raw value\n//\n// Public surface lives on `FormController` as `format()` and\n// `formatAll()`. Both are chainable and both behave identically — the\n// alias exists only to read naturally when the caller wants to apply\n// the same configuration to several inputs.\n//\n// When `@samline/formatter` is not installed the methods log a single\n// `console.error` (via `loadFormatter`) and return the controller\n// unchanged so the rest of the app keeps working.\n\nimport 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\n// Listeners added on demand; tracked here so `destroy()` can detach\n// the input listeners and remove the mirrors the controller owns.\ntype FormatEntry = {\n canonicalName: string\n displayName: string\n visible: HTMLInputElement | HTMLTextAreaElement\n mirror: HTMLInputElement\n mirrorIsOwned: boolean\n /**\n * The bound input listener. `null` until phase 2 of `applyFormat`\n * installs the real closure (after the formatter peer resolves);\n * the placeholder skips input events so the visible / mirror\n * remain consistent even before the peer loads.\n */\n handler: ((event: Event) => void) | null\n}\n\n/**\n * Sentinel for \"no handler bound yet\" so phase 2 of `applyFormat`\n * can tell first-time bindings apart from idempotent re-binds.\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 // `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: EventListener\n capture?: boolean\n } = { element: target, type, handler }\n if (useCapture !== undefined) entry.capture = useCapture\n state.listeners.push(entry)\n}\n\n// `InputEvent.inputType` is the canonical signal for \"what just\n// happened\". The listener is registered as a plain `Event` handler so\n// older / synthetic events without `inputType` simply fall through and\n// behave as insertions — which preserves the pre-existing cursor math.\nconst readInputType = (event: Event): string | undefined => {\n const candidate = event as Partial<InputEvent>\n return typeof candidate.inputType === 'string' ? candidate.inputType : undefined\n}\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 // Two valid event sources: the visible (user typing) or the\n // mirror (a `setValue('phone', x)` or external script that\n // wrote the raw). Any other source is unrelated.\n const isVisible = event.target === visible\n const isMirror = event.target === mirror\n if (!isVisible && !isMirror) return\n\n // When the mirror is the source, take its current value as the\n // raw input — it is the developer's authoritative intent. When\n // the visible is the source, take the visible's value as usual\n // (it is the unformatted text the user just typed).\n const rawInput = isMirror ? mirror.value : visible.value\n const inputType = readInputType(event)\n const { formatted, raw } = formatFn(rawInput, formatType, formatOptions)\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// Internal: core apply routine shared by `format()` and `formatAll()`.\n// Splits the setup into two phases so the developer-facing effects\n// (visible renamed, hidden created) are visible synchronously after\n// `format()` returns. The formatter peer still loads asynchronously\n// on the first call, but only the \"bind input listener + apply\n// formatter to the current value\" step is deferred — every other\n// piece of state is in place by the time the method returns.\n//\n// Phase 2 only touches the entries for the fields in THIS\n// `applyFormat` call — never iterates the whole bucket. Multiple\n// concurrent `format()` calls (e.g. `format(phone)`, `format(date)`,\n// `format(general)` in the same script tick) each have their own\n// phase 2, and each binds its own entry with its own config. If\n// phase 2 walked the whole bucket, the first call to resume would\n// bind every entry with the first call's config and the rest would\n// short-circuit on the \"already bound\" check, leaving subsequent\n// fields wired to the wrong formatter.\n//\n// Returns the controller to preserve chainability even on the missing\n// peer path.\nconst applyFormat = async (\n state: FormControllerState,\n helpers: FormControllerHelpers,\n config: FieldFormatConfig\n): Promise<void> => {\n if (!state.element || !state.api) return\n\n const formatType = config.type\n const formatOptions = config.options\n const fieldNames = resolveFieldNames(config)\n const bucket = getRegistry(state)\n\n // Track the entries phase 1 just inserted (or re-bound) so\n // phase 2 only touches those — never the entries of a previous\n // `format()` call. Without this, the first call to resume from\n // `await loadFormatter()` would bind every entry in the bucket\n // with the first call's config.\n const phase1Entries: FormatEntry[] = []\n\n // Phase 1 (sync): rename the visible, create / reuse the hidden\n // mirror, refresh the `formattedFields` registry. After this\n // returns, the controller's API surface is fully functional for\n // the field — `getValue('phone')` reads the hidden, `getValue(\n // 'phone_displayed')` reads the visible. The input listener and\n // the initial value pass through the formatter are installed\n // once the peer resolves in phase 2.\n for (const fieldName of fieldNames) {\n const displayName = config.displayField ?? `${fieldName}_displayed`\n\n // Resolve the visible input. Two authoring styles are supported:\n //\n // 1. The dev wrote `<input name=\"phone\">` (the canonical\n // name). `format()` will rename it to `phone_displayed`\n // on first run.\n // 2. The dev pre-authored `<input name=\"phone_displayed\">`\n // (or whatever `displayField` resolves to). `format()`\n // leaves the name alone.\n //\n // Both paths end up with the same DOM: a visible carrying the\n // display name + a hidden carrying the canonical name.\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 // Idempotency: a second `format()` call for the same canonical\n // name refreshes the configuration but does not double-bind\n // listeners or duplicate the hidden mirror.\n const existing = bucket.get(fieldName)\n\n // Pre-authored hidden? Reuse it. Otherwise create one.\n let mirror = findHiddenMirror(state.element, fieldName)\n const mirrorIsOwned = !mirror\n if (!mirror) mirror = ensureHiddenMirror(state.element, fieldName)\n\n // Rename the visible from the canonical name to the display\n // name. No-op if the developer pre-authored the visible with\n // the display name (skip-rename path).\n renameVisibleField(visible, displayName)\n\n if (existing) {\n // Refresh the stored config so `destroy()` knows the entry\n // is still active, but do not re-attach listeners.\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 // Reserve the slot in the bucket so subsequent\n // `getFieldsByName` lookups can find the visible + mirror\n // and so phase 2 has a target to bind the handler to.\n // The handler is installed in phase 2 (after the formatter\n // peer resolves); until then the entry is tracked but the\n // capture-phase input listener is not yet on the form.\n const entry: FormatEntry = {\n canonicalName: fieldName,\n displayName,\n visible,\n mirror,\n mirrorIsOwned,\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 (async): load the formatter peer (cached on second\n // call) and install the real input listener + initial-value\n // pass. If the peer is missing, every entry phase 1 created is\n // rolled back: the visible's name is restored, the owned hidden\n // mirror is removed, the bucket + registry are cleared. The user\n // sees a single `console.error` from `loadFormatter` and the form\n // is left exactly as the developer authored it.\n const formatter = await loadFormatter()\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 // Idempotent re-bind path (e.g. the country_code watch\n // re-calling `format({ type: 'phone', field: 'phone' })`\n // with a new country): re-format the current value with\n // the new options. The handler is already installed with\n // its original closure; we do not re-bind it on every\n // re-call because the listener was bound with capture and\n // updating it would require detach + reattach, which is\n // out of scope for v2.3. If you need hot-swap semantics,\n // call `destroy()` + `format()` again.\n const current = entry.visible.value\n if (current !== '') {\n const { formatted, raw } = formatter.format(current, formatType, formatOptions)\n applyFormattedValue(entry.visible, entry.mirror, formatted, raw)\n }\n continue\n }\n\n // First-time bind: install the real capture-phase handler\n // and run the initial value through the formatter so\n // pre-filled data (set before format() ran) is normalised\n // and the hidden mirror is populated without waiting for a\n // user keystroke.\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 const { formatted, raw } = formatter.format(initial, formatType, formatOptions)\n applyFormattedValue(entry.visible, entry.mirror, formatted, raw)\n } else if (entry.mirrorIsOwned && entry.mirror.value !== '') {\n // Only clear an owned mirror when the visible is empty.\n // Pre-existing mirrors are left untouched so their values\n // are preserved.\n entry.mirror.value = ''\n }\n }\n}\n\n// Roll back every change made in phase 1 of `applyFormat`. Used\n// when the formatter peer is missing — we promised the user that\n// `format()` is a no-op in that case, so we have to put the DOM\n// back the way it was before the sync rename + hidden creation.\n//\n// Only the entries that the current `applyFormat` call touched\n// are rolled back; pre-existing entries from previous calls are\n// left alone.\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 // Fire and forget: the loader surfaces a single console.error if\n // the peer is missing, then resolves with `null`.\n void applyFormat(state, helpers, config)\n\n return state.api!\n }\n\nexport const createFormatAll = createFormat\n\n// Expose the lookup builder for the controller's delegated event\n// handler. It maps either name (canonical or display) to the\n// canonical so watchers on either name resolve to the same formatted\n// pair.\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\n// Cleanup hook consumed by `api/destroy.ts`. Removes every listener\n// registered through this module, restores the visible's name to the\n// canonical, and drops the owned hidden mirrors that were created\n// during the controller's lifetime. Pre-existing mirrors and the\n// developer's original HTML are left untouched.\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 // Restore the visible's name so the form goes back to the\n // developer's authored state.\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 // Catch any leftover owned mirrors (defensive — should not happen\n // because every `applyFormat` path registers in the bucket, but the\n // second pass guarantees no orphans escape `destroy()`).\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 state.watchedFields.clear()\n state.subscribers.clear()\n state.submitHandlers.clear()\n state.fieldCache.clear()\n state.manualErrors = {}\n state.validationErrors = {}\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 const current = data[key]\n if (current === undefined) {\n data[key] = value\n return\n }\n if (Array.isArray(current)) {\n current.push(value)\n return\n }\n data[key] = [current, value]\n}\n\nexport const parseFormData = (formElement: HTMLFormElement): SerializedFormResult => {\n const raw = 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 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 isValidated: boolean\n autoSubmitEnabled: boolean\n autoSubmitDebounce: number\n submitCount: 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 // 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 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 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\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.querySelectorAll('input[name], select[name], textarea[name]')).filter(\n isFormFieldElement\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 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 (const field of fields) {\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 field.value = normalizedValue\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 form\n .querySelectorAll(`[${attribute}]`)\n .forEach(node => node.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'\n\nexport const createReset =\n (state: FormControllerState, helpers: FormControllerHelpers) =>\n () => {\n if (!state.element) return state.api!\n\n state.element.reset()\n state.manualErrors = {}\n state.validationErrors = {}\n clearAttributes(state.element, [state.attributes.error, state.attributes.filled])\n\n if (state.isValidated) 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 const eventType =\n firstField instanceof HTMLSelectElement ||\n (firstField instanceof HTMLInputElement &&\n (firstField.type === 'checkbox' || firstField.type === 'radio'))\n ? 'change'\n : 'input'\n\n firstField.dispatchEvent(new Event(eventType, { 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 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\nexport const validateFieldValue = (\n field: string,\n value: FormFieldValue,\n rules: FieldValidationRules,\n values: FormValues\n): string[] => {\n const errors: string[] = []\n const context: FieldValidationContext = { field, value, values }\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\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) && !pattern.value.test(toPatternTarget(value))) {\n errors.push(pattern.message ?? 'Value does not match the required pattern.')\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 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 { createAutoSubmit } from '../api/auto-submit'\nimport { createClearErrors } from '../api/clear-errors'\nimport { createDestroy } from '../api/destroy'\nimport { createDisableAutoSubmit } from '../api/disable-auto-submit'\nimport { createFormat, createFormatAll, resolveCanonicalForName } 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 { validateFieldValue } from '../core/validation'\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 isValidated: Boolean(normalizedOptions.autoValidate),\n autoSubmitEnabled: false,\n autoSubmitDebounce: 0,\n submitCount: 0,\n autoSubmitTimer: null,\n isDestroyed: false,\n listeners: [],\n mutationObserver: null,\n formattedFields: new Map(),\n api: null\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 if (!state.fieldCache.has(name)) {\n state.fieldCache.set(name, queryNamedFields(state.element, name))\n }\n return state.fieldCache.get(name) ?? []\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 const targetNames = names ?? getTrackedFieldNames()\n const errors = getMergedErrors()\n for (const name of targetNames) {\n const fields = getFieldsByName(name)\n const hasError = Boolean(errors[name]?.length)\n for (const field of fields) {\n applyBooleanAttribute(field, attributes.filled, isFieldFilled(field))\n applyBooleanAttribute(field, attributes.error, hasError)\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\n for (const name of targetNames) {\n const rules = state.validators[name]\n if (!rules) {\n delete nextValidationErrors[name]\n continue\n }\n const messages = validateFieldValue(name, values[name], rules, values)\n if (messages.length > 0) nextValidationErrors[name] = messages\n else delete nextValidationErrors[name]\n }\n\n state.validationErrors = nextValidationErrors\n syncVisualState(names)\n\n return {\n isValid: Object.keys(getMergedErrors()).length === 0,\n errors: cloneErrors(getMergedErrors())\n }\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. Watchers on both names still fire\n // below — each gets the value of the name it was registered\n // against, so a `watch('phone', cb)` callback receives the raw\n // and a `watch('phone_displayed', cb)` callback receives the\n // formatted.\n const canonical = resolveCanonicalForName(state, name) ?? name\n\n if (normalizedOptions.clearManualErrorsOnChange) {\n delete state.manualErrors[canonical]\n }\n\n syncVisualState([canonical])\n\n if (state.isValidated && state.validators[canonical]) {\n validateNames([canonical])\n }\n\n emitFieldWatchers(canonical)\n if (canonical !== name) emitFieldWatchers(name)\n notifySubscribers()\n scheduleAutoSubmit()\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) return\n\n const { data, formData } = state.api.getData()\n const snapshot = state.api.getState()\n handlers.forEach(handler =>\n handler.callback(state.element!, data, formData, snapshot)\n )\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 syncVisualState()\n validateNames()\n }\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 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(state.element, 'change', handleDelegatedEvent)\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) {\n syncVisualState()\n validateNames()\n }\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 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}"],"mappings":"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","names":["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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@samline/forms",
3
- "version": "2.2.3",
3
+ "version": "2.3.1",
4
4
  "description": "Form controller for vanilla JS and direct browser usage.",
5
5
  "type": "module",
6
6
  "license": "MIT",