@samline/forms 2.1.0 → 2.2.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/README.md +6 -3
- package/dist/browser/global.d.ts +2 -1
- package/dist/browser/global.global.js +5 -2
- package/dist/browser/global.global.js.map +1 -1
- package/dist/index.cjs +34 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -42,10 +42,10 @@ Requires Node 20+ when bundling. Runtime target is ES2020.
|
|
|
42
42
|
Use the browser build when you do not have a bundler and need to run the package directly in HTML, Shopify, WordPress, or any traditional template.
|
|
43
43
|
|
|
44
44
|
```html
|
|
45
|
-
<script src="https://unpkg.com/@samline/forms@2.
|
|
45
|
+
<script src="https://unpkg.com/@samline/forms@2.2.0/dist/browser/global.global.js"></script>
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
> Pin the version in production. Replace `2.
|
|
48
|
+
> Pin the version in production. Replace `2.2.0` with the version you ship.
|
|
49
49
|
|
|
50
50
|
The browser bundle exposes a single global: `window.Forms`.
|
|
51
51
|
|
|
@@ -55,7 +55,7 @@ The browser bundle exposes a single global: `window.Forms`.
|
|
|
55
55
|
<button type="submit">Send</button>
|
|
56
56
|
</form>
|
|
57
57
|
|
|
58
|
-
<script src="https://unpkg.com/@samline/forms@2.
|
|
58
|
+
<script src="https://unpkg.com/@samline/forms@2.2.0/dist/browser/global.global.js"></script>
|
|
59
59
|
<script>
|
|
60
60
|
const contactForm = window.Forms.newForm({ id: 'contact-form' })
|
|
61
61
|
|
|
@@ -80,6 +80,8 @@ See [docs/browser.md](docs/browser.md) for the full browser surface.
|
|
|
80
80
|
| `@samline/forms` | Main vanilla API for bundlers, ESM, or CJS consumers. |
|
|
81
81
|
| `@samline/forms/browser` | Pre-bundled IIFE that registers `window.Forms` for direct `<script>` usage. |
|
|
82
82
|
|
|
83
|
+
The vanilla entrypoint also exports `browser`, the same `{ form, newForm, destroyForm, available }` surface as the IIFE but as a module-level singleton (no `globalThis` side-effect). Use it from a bundler when you want the registry helpers without the IIFE — see [docs/browser.md → Using the same shape from a bundler](docs/browser.md#using-the-same-shape-from-a-bundler).
|
|
84
|
+
|
|
83
85
|
---
|
|
84
86
|
|
|
85
87
|
## Quick Start
|
|
@@ -132,6 +134,7 @@ The controller is built around one factory and a small set of focused methods. M
|
|
|
132
134
|
| Group | Methods |
|
|
133
135
|
| --- | --- |
|
|
134
136
|
| Lifecycle | [`form`](docs/api/form.md) · [`destroy`](docs/api/destroy.md) · [`reset`](docs/api/reset.md) |
|
|
137
|
+
| Registry (vanilla) | [`browser`](docs/getting-started.md#browser-registry-helpers) — bundler-friendly `{ form, newForm, destroyForm, available }` singleton. |
|
|
135
138
|
| Properties | [`element`](docs/api/element.md) · [`options`](docs/options.md) |
|
|
136
139
|
| Submission | [`onSubmit`](docs/api/on-submit.md) · [`autoSubmit`](docs/api/auto-submit.md) · [`disableAutoSubmit`](docs/api/disable-auto-submit.md) |
|
|
137
140
|
| Field observation | [`watch`](docs/api/watch.md) · [`observe`](docs/api/observe.md) · [`unwatch`](docs/api/unwatch.md) · [`subscribe`](docs/api/subscribe.md) |
|
package/dist/browser/global.d.ts
CHANGED
|
@@ -142,6 +142,7 @@ interface FormsApi {
|
|
|
142
142
|
destroyForm: (id: string) => void;
|
|
143
143
|
available: FormsAvailable;
|
|
144
144
|
}
|
|
145
|
+
|
|
145
146
|
declare const Forms: FormsApi;
|
|
146
147
|
declare global {
|
|
147
148
|
interface Window {
|
|
@@ -149,4 +150,4 @@ declare global {
|
|
|
149
150
|
}
|
|
150
151
|
}
|
|
151
152
|
|
|
152
|
-
export {
|
|
153
|
+
export { Forms as default };
|
|
@@ -1000,7 +1000,7 @@ var Forms = (() => {
|
|
|
1000
1000
|
// src/api/form.ts
|
|
1001
1001
|
var form = (target, options = {}) => createFormController(target, options);
|
|
1002
1002
|
|
|
1003
|
-
// src/browser/
|
|
1003
|
+
// src/browser/registry.ts
|
|
1004
1004
|
var available = {};
|
|
1005
1005
|
var newForm = (input) => {
|
|
1006
1006
|
const { id, options } = input;
|
|
@@ -1025,12 +1025,15 @@ var Forms = (() => {
|
|
|
1025
1025
|
console.warn(`Form with ID ${id} not found`);
|
|
1026
1026
|
}
|
|
1027
1027
|
};
|
|
1028
|
-
var
|
|
1028
|
+
var browser = {
|
|
1029
1029
|
form,
|
|
1030
1030
|
newForm,
|
|
1031
1031
|
destroyForm,
|
|
1032
1032
|
available
|
|
1033
1033
|
};
|
|
1034
|
+
|
|
1035
|
+
// src/browser/global.ts
|
|
1036
|
+
var Forms = browser;
|
|
1034
1037
|
if (typeof globalThis !== "undefined") {
|
|
1035
1038
|
;
|
|
1036
1039
|
globalThis.Forms = Forms;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/browser/global.ts","../../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"],"sourcesContent":["// Browser entrypoint. Exposes a minimal surface as a single global\n// accessible via `window.Forms` (or `globalThis.Forms`).\n//\n// Public shape:\n// - Forms.form(target, options)\n// - Forms.newForm({ id, options })\n// - Forms.destroyForm(id)\n// - Forms.available (registry keyed by form id)\n\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\nconst Forms: FormsApi = {\n form,\n newForm,\n destroyForm,\n available\n}\n\ndeclare global {\n interface Window {\n Forms: FormsApi\n }\n}\n\nif (typeof globalThis !== 'undefined') {\n ;(globalThis as typeof globalThis & { Forms: FormsApi }).Forms = Forms\n}\n\nexport default Forms","// 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// - 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`).\nexport const applyFormattedValue = (\n visible: HTMLInputElement | HTMLTextAreaElement,\n mirror: HTMLInputElement | null,\n formatted: string,\n raw: 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 (rawInput === formatted) {\n if (mirror && mirror.value !== raw) mirror.value = raw\n return caret\n }\n\n visible.value = formatted\n\n if (mirror) mirror.value = raw\n\n const newCaret = computeCursorPosition(caret, rawInput, formatted)\n restoreCursor(visible, newCaret)\n return newCaret\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 build graph.\n // The `/* @vite-ignore */` comment prevents bundlers (vite/rollup)\n // from trying to pre-bundle the optional peer.\n const mod = (await import(\n /* @vite-ignore */ '@samline/formatter'\n )) 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 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)\n // Keep the registry mirror pointer alive for in-place cleanup.\n void mirrorFieldName\n }\n return handler\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"],"mappings":"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","names":["state","state","state","form","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/browser/global.ts","../../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":["// Browser entrypoint. Exposes a minimal surface as a single global\n// accessible via `window.Forms` (or `globalThis.Forms`).\n//\n// Public shape:\n// - Forms.form(target, options)\n// - Forms.newForm({ id, options })\n// - Forms.destroyForm(id)\n// - Forms.available (registry keyed by form id)\n//\n// The IIFE bundle consumes the same `browser` singleton as the vanilla\n// entrypoint — single source of truth for the registry helpers.\n\nimport { browser } from './registry'\nimport type { FormsApi } from './registry'\n\nconst Forms: FormsApi = browser\n\ndeclare global {\n interface Window {\n Forms: FormsApi\n }\n}\n\nif (typeof globalThis !== 'undefined') {\n ;(globalThis as typeof globalThis & { Forms: FormsApi }).Forms = Forms\n}\n\nexport default Forms","// 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// - 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`).\nexport const applyFormattedValue = (\n visible: HTMLInputElement | HTMLTextAreaElement,\n mirror: HTMLInputElement | null,\n formatted: string,\n raw: 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 (rawInput === formatted) {\n if (mirror && mirror.value !== raw) mirror.value = raw\n return caret\n }\n\n visible.value = formatted\n\n if (mirror) mirror.value = raw\n\n const newCaret = computeCursorPosition(caret, rawInput, formatted)\n restoreCursor(visible, newCaret)\n return newCaret\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 build graph.\n // The `/* @vite-ignore */` comment prevents bundlers (vite/rollup)\n // from trying to pre-bundle the optional peer.\n const mod = (await import(\n /* @vite-ignore */ '@samline/formatter'\n )) 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 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)\n // Keep the registry mirror pointer alive for in-place cleanup.\n void mirrorFieldName\n }\n return handler\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","state","state","state","state","state","form","state","state","state","state","state","state","state","state","state","state","state","state","addListener","target"]}
|
package/dist/index.cjs
CHANGED
|
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var src_exports = {};
|
|
32
32
|
__export(src_exports, {
|
|
33
|
+
browser: () => browser,
|
|
33
34
|
createFormController: () => createFormController,
|
|
34
35
|
form: () => form,
|
|
35
36
|
parseFormData: () => parseFormData,
|
|
@@ -1008,8 +1009,41 @@ var createFormController = (target, options = {}) => {
|
|
|
1008
1009
|
|
|
1009
1010
|
// src/api/form.ts
|
|
1010
1011
|
var form = (target, options = {}) => createFormController(target, options);
|
|
1012
|
+
|
|
1013
|
+
// src/browser/registry.ts
|
|
1014
|
+
var available = {};
|
|
1015
|
+
var newForm = (input) => {
|
|
1016
|
+
const { id, options } = input;
|
|
1017
|
+
if (!id) {
|
|
1018
|
+
console.error("Form ID is required");
|
|
1019
|
+
return;
|
|
1020
|
+
}
|
|
1021
|
+
const controller = form(id, { ...options });
|
|
1022
|
+
available[id] = controller;
|
|
1023
|
+
return controller;
|
|
1024
|
+
};
|
|
1025
|
+
var destroyForm = (id) => {
|
|
1026
|
+
if (!id) {
|
|
1027
|
+
console.error("Form ID is required");
|
|
1028
|
+
return;
|
|
1029
|
+
}
|
|
1030
|
+
const controller = available[id];
|
|
1031
|
+
if (controller) {
|
|
1032
|
+
controller.destroy();
|
|
1033
|
+
delete available[id];
|
|
1034
|
+
} else {
|
|
1035
|
+
console.warn(`Form with ID ${id} not found`);
|
|
1036
|
+
}
|
|
1037
|
+
};
|
|
1038
|
+
var browser = {
|
|
1039
|
+
form,
|
|
1040
|
+
newForm,
|
|
1041
|
+
destroyForm,
|
|
1042
|
+
available
|
|
1043
|
+
};
|
|
1011
1044
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1012
1045
|
0 && (module.exports = {
|
|
1046
|
+
browser,
|
|
1013
1047
|
createFormController,
|
|
1014
1048
|
form,
|
|
1015
1049
|
parseFormData,
|