auto-webmcp 0.3.20 → 0.3.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auto-webmcp.cjs.js +44 -8
- package/dist/auto-webmcp.cjs.js.map +2 -2
- package/dist/auto-webmcp.esm.js +44 -8
- package/dist/auto-webmcp.esm.js.map +2 -2
- package/dist/auto-webmcp.iife.js +1 -1
- package/dist/auto-webmcp.iife.js.map +3 -3
- package/dist/interceptor.d.ts +0 -9
- package/dist/interceptor.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts", "../src/config.ts", "../src/schema.ts", "../src/analyzer.ts", "../src/registry.ts", "../src/interceptor.ts", "../src/discovery.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * index.ts \u2014 Entry point & public API for auto-webmcp\n *\n * Zero-config drop-in:\n * <script src=\"auto-webmcp.iife.js\"></script>\n *\n * ESM usage:\n * import { autoWebMCP } from 'auto-webmcp';\n * autoWebMCP({ exclude: ['#login-form'] });\n */\n\nimport { AutoWebMCPConfig, resolveConfig } from './config.js';\nimport { startDiscovery, stopDiscovery } from './discovery.js';\nimport { unregisterAll, getAllRegisteredTools, isWebMCPSupported } from './registry.js';\n\nexport type { AutoWebMCPConfig } from './config.js';\nexport type { ToolMetadata } from './analyzer.js';\nexport type { JsonSchema, JsonSchemaProperty } from './schema.js';\n\nexport interface AutoWebMCPHandle {\n /** Stop observing and unregister all tools */\n destroy: () => Promise<void>;\n /** Return all currently registered tools */\n getTools: () => Array<{ form: HTMLFormElement; name: string }>;\n /** True if running in a WebMCP-capable browser */\n isSupported: boolean;\n}\n\n/**\n * Initialize auto-webmcp.\n *\n * @param config \u2014 Optional configuration (all fields optional)\n * @returns A handle to inspect or tear down the instance\n */\nexport async function autoWebMCP(config?: AutoWebMCPConfig): Promise<AutoWebMCPHandle> {\n const resolved = resolveConfig(config);\n\n if (resolved.debug) {\n console.debug('[auto-webmcp] Initializing', {\n webmcpSupported: isWebMCPSupported(),\n config: resolved,\n });\n }\n\n await startDiscovery(resolved);\n\n return {\n destroy: async () => {\n stopDiscovery();\n await unregisterAll();\n },\n getTools: getAllRegisteredTools,\n isSupported: isWebMCPSupported(),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Auto-init for IIFE / script-tag usage\n// ---------------------------------------------------------------------------\n\n// When loaded as a <script> tag, auto-initialize with zero config.\n// Users can prevent this by setting window.__AUTO_WEBMCP_NO_AUTOINIT = true\n// before the script loads.\n\nif (\n typeof window !== 'undefined' &&\n !(window as unknown as Record<string, unknown>)['__AUTO_WEBMCP_NO_AUTOINIT']\n) {\n void autoWebMCP();\n}\n", "/**\n * config.ts \u2014 User configuration merging & defaults\n */\n\nexport interface FormOverride {\n name?: string;\n description?: string;\n}\n\nexport interface AutoWebMCPConfig {\n /**\n * CSS selectors for forms to skip. E.g. ['#login-form', '[data-no-webmcp]']\n */\n exclude?: string[];\n\n /**\n * If true, agent-invoked forms are auto-submitted without human confirmation.\n * Default: false\n */\n autoSubmit?: boolean;\n\n /**\n * How to handle forms that already use native declarative WebMCP attributes.\n * - skip: do not imperatively register forms with toolname\n * - augment: reserved for future metadata-only enhancement (currently behaves like skip)\n * - force: always register imperatively even when toolname exists\n * Default: skip\n */\n declarativeMode?: 'skip' | 'augment' | 'force';\n\n /**\n * Parameter binding behavior for execute() input keys.\n */\n paramBinding?: {\n /**\n * If true, resolve semantically similar parameter keys to schema keys.\n * Default: true\n */\n enableAliasResolution?: boolean;\n /**\n * If true, only exact schema keys are accepted (disables alias resolution).\n * Default: false\n */\n strict?: boolean;\n };\n\n /**\n * Execution behavior controls.\n */\n execution?: {\n /**\n * Max time to wait for submit/result before returning a deterministic timeout state.\n * Default: 15000\n */\n timeoutMs?: number;\n };\n\n /**\n * Per-form name/description overrides keyed by CSS selector.\n */\n overrides?: Record<string, FormOverride>;\n\n /**\n * When true, skip filling any form field that already has a non-empty value.\n * Useful for forms with pre-populated defaults (e.g. Salesforce Lightning) where\n * the agent should only fill blank fields. Default: false\n */\n preserveExisting?: boolean;\n\n /**\n * Log registered tools to console on init. Default: false\n */\n debug?: boolean;\n}\n\nexport interface ResolvedConfig {\n exclude: string[];\n autoSubmit: boolean;\n declarativeMode: 'skip' | 'augment' | 'force';\n paramBinding: {\n enableAliasResolution: boolean;\n strict: boolean;\n };\n execution: {\n timeoutMs: number;\n };\n overrides: Record<string, FormOverride>;\n preserveExisting: boolean;\n debug: boolean;\n}\n\nexport function resolveConfig(userConfig?: AutoWebMCPConfig): ResolvedConfig {\n const strict = userConfig?.paramBinding?.strict ?? false;\n const enableAliasResolution =\n strict ? false : (userConfig?.paramBinding?.enableAliasResolution ?? true);\n return {\n exclude: userConfig?.exclude ?? [],\n autoSubmit: userConfig?.autoSubmit ?? false,\n declarativeMode: userConfig?.declarativeMode ?? 'skip',\n paramBinding: {\n strict,\n enableAliasResolution,\n },\n execution: {\n timeoutMs: Math.max(100, userConfig?.execution?.timeoutMs ?? 15000),\n },\n overrides: userConfig?.overrides ?? {},\n preserveExisting: userConfig?.preserveExisting ?? false,\n debug: userConfig?.debug ?? false,\n };\n}\n", "/**\n * schema.ts \u2014 HTML input type \u2192 JSON Schema type mapping\n */\n\nexport const ARIA_ROLES_TO_SCAN = [\n 'textbox', 'combobox', 'checkbox', 'radio', 'switch',\n 'spinbutton', 'searchbox', 'slider',\n] as const;\n\nexport type AriaRole = typeof ARIA_ROLES_TO_SCAN[number];\n\nexport interface JsonSchemaProperty {\n type: string;\n format?: string;\n description?: string;\n title?: string;\n enum?: string[];\n oneOf?: Array<{ const: string; title: string; group?: string }>;\n items?: { type: string; enum?: string[] };\n minimum?: number;\n maximum?: number;\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n default?: unknown;\n}\n\nexport interface JsonSchema {\n $schema?: string;\n type: 'object';\n properties: Record<string, JsonSchemaProperty>;\n required: string[];\n}\n\n/** Maps an HTML <input type> to a JSON Schema property base */\nexport function inputTypeToSchema(\n input: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement,\n): JsonSchemaProperty | null {\n if (input instanceof HTMLInputElement) {\n return mapInputElement(input);\n }\n if (input instanceof HTMLTextAreaElement) {\n return { type: 'string' };\n }\n if (input instanceof HTMLSelectElement) {\n return mapSelectElement(input);\n }\n return null;\n}\n\nfunction mapInputElement(input: HTMLInputElement): JsonSchemaProperty | null {\n const type = input.type.toLowerCase();\n\n switch (type) {\n case 'text':\n case 'search':\n case 'tel':\n return buildStringSchema(input);\n\n case 'email':\n return { ...buildStringSchema(input), format: 'email' };\n\n case 'url':\n return { ...buildStringSchema(input), format: 'uri' };\n\n case 'number':\n case 'range': {\n const prop: JsonSchemaProperty = { type: 'number' };\n if (input.min !== '') prop.minimum = parseFloat(input.min);\n if (input.max !== '') prop.maximum = parseFloat(input.max);\n return prop;\n }\n\n case 'date':\n return { type: 'string', format: 'date' };\n\n case 'datetime-local':\n return { type: 'string', format: 'date-time' };\n\n case 'time':\n return { type: 'string', format: 'time' };\n\n case 'month':\n return { type: 'string', pattern: '^\\\\d{4}-\\\\d{2}$' };\n\n case 'week':\n return { type: 'string', pattern: '^\\\\d{4}-W\\\\d{2}$' };\n\n case 'color':\n return { type: 'string', pattern: '^#[0-9a-fA-F]{6}$' };\n\n case 'checkbox':\n return { type: 'boolean' };\n\n case 'radio':\n // Radio groups are handled at the form level in analyzer.ts\n return { type: 'string' };\n\n case 'file':\n case 'hidden':\n case 'submit':\n case 'reset':\n case 'button':\n case 'image':\n // These are not exposed to agents\n return null;\n\n case 'password':\n // Skip passwords \u2014 never expose to agents\n return null;\n\n default:\n return { type: 'string' };\n }\n}\n\nfunction buildStringSchema(input: HTMLInputElement): JsonSchemaProperty {\n const prop: JsonSchemaProperty = { type: 'string' };\n if (input.minLength > 0) prop.minLength = input.minLength;\n if (input.maxLength > 0 && input.maxLength !== 524288) prop.maxLength = input.maxLength;\n if (input.pattern) prop.pattern = input.pattern;\n\n // Expose <datalist> suggestions as enum/oneOf so agents know the preferred values.\n // The field stays type:string because datalist is advisory, not restrictive.\n const listId = input.getAttribute('list');\n if (listId) {\n const datalist = input.ownerDocument.getElementById(listId);\n if (datalist instanceof HTMLDataListElement) {\n const options = Array.from(datalist.options).filter(\n (o) => !o.disabled && o.value.trim() !== '',\n );\n if (options.length > 0) {\n prop.enum = options.map((o) => o.value.trim());\n prop.oneOf = options.map((o) => ({\n const: o.value.trim(),\n title: o.textContent?.trim() || o.value.trim(),\n }));\n }\n }\n }\n\n return prop;\n}\n\n/**\n * Matches common placeholder/prompt option text patterns so that options like\n * \"-- Select a size --\" or \"Choose region\" are excluded from the schema enum\n * while genuinely meaningful empty-value options like \"No preference\" are kept.\n */\nconst PLACEHOLDER_PATTERNS = /^(select|choose|pick)\\b|^--+|---/i;\n\n/**\n * Returns true when an option should be treated as a UI placeholder that agents\n * should not select. An option is a placeholder when it is explicitly disabled,\n * or when its submission value is empty and its visible text matches common\n * prompt patterns (\"Select...\", \"Choose...\", \"Pick...\", \"-- ... --\").\n *\n * Options with empty values that carry real meaning (\"No preference\", \"Any\",\n * \"All Categories\") are NOT considered placeholders and will be included.\n */\nfunction isPlaceholderOption(opt: HTMLOptionElement): boolean {\n if (opt.disabled) return true;\n if (opt.value !== '') return false;\n return PLACEHOLDER_PATTERNS.test(opt.text.trim());\n}\n\nfunction mapSelectElement(select: HTMLSelectElement): JsonSchemaProperty {\n const enumValues: string[] = [];\n const oneOf: Array<{ const: string; title: string; group?: string }> = [];\n\n for (const child of Array.from(select.children)) {\n if (child instanceof HTMLOptGroupElement) {\n if (child.disabled) continue;\n const groupLabel = child.label?.trim() ?? '';\n for (const opt of Array.from(child.children)) {\n if (!(opt instanceof HTMLOptionElement)) continue;\n if (isPlaceholderOption(opt)) continue;\n enumValues.push(opt.value);\n const entry: { const: string; title: string; group?: string } = {\n const: opt.value,\n title: opt.text.trim() || opt.value,\n };\n if (groupLabel) entry.group = groupLabel;\n oneOf.push(entry);\n }\n } else if (child instanceof HTMLOptionElement) {\n if (isPlaceholderOption(child)) continue;\n enumValues.push(child.value);\n oneOf.push({ const: child.value, title: child.text.trim() || child.value });\n }\n }\n\n if (enumValues.length === 0) return { type: 'string' };\n\n if (select.multiple) {\n // Multi-select: agent passes an array of selected values\n return { type: 'array', items: { type: 'string', enum: enumValues } };\n }\n\n return { type: 'string', enum: enumValues, oneOf };\n}\n\n/** Collect all checkbox values for a given name within a form (for checkbox groups) */\nexport function collectCheckboxEnum(form: HTMLFormElement, name: string): string[] {\n return Array.from(form.elements)\n .filter(\n (el): el is HTMLInputElement =>\n el instanceof HTMLInputElement &&\n el.type === 'checkbox' &&\n el.name === name,\n )\n .map((cb) => cb.value)\n .filter((v) => v !== '' && v !== 'on'); // 'on' is the browser default when no value attr is set\n}\n\n/** Collect all radio button values for a given name within a form */\nexport function collectRadioEnum(form: HTMLFormElement, name: string): string[] {\n const radios = Array.from(form.elements).filter(\n (el): el is HTMLInputElement =>\n el instanceof HTMLInputElement &&\n el.type === 'radio' &&\n el.name === name,\n );\n return radios.map((r) => r.value).filter((v) => v !== '');\n}\n\n/** Collect radio button values + label titles as oneOf entries */\nexport function collectRadioOneOf(\n form: HTMLFormElement,\n name: string,\n): Array<{ const: string; title: string }> {\n const radios = Array.from(form.elements)\n .filter(\n (el): el is HTMLInputElement =>\n el instanceof HTMLInputElement &&\n el.type === 'radio' &&\n el.name === name,\n )\n .filter((r) => r.value !== '');\n\n return radios.map((r) => {\n const title = getRadioLabelText(r);\n return { const: r.value, title: title || r.value };\n });\n}\n\n/** Maps an ARIA role element to a JSON Schema property */\nexport function ariaRoleToSchema(el: Element, role: AriaRole): JsonSchemaProperty {\n switch (role) {\n case 'checkbox':\n case 'switch':\n return { type: 'boolean' };\n\n case 'spinbutton':\n case 'slider': {\n const prop: JsonSchemaProperty = { type: 'number' };\n const min = el.getAttribute('aria-valuemin');\n const max = el.getAttribute('aria-valuemax');\n if (min !== null) prop.minimum = parseFloat(min);\n if (max !== null) prop.maximum = parseFloat(max);\n return prop;\n }\n\n case 'combobox': {\n const ownedId = el.getAttribute('aria-owns') ?? el.getAttribute('aria-controls');\n if (ownedId) {\n const listbox = document.getElementById(ownedId);\n if (listbox) {\n const options = Array.from(listbox.querySelectorAll('[role=\"option\"]')).filter(\n (o) => o.getAttribute('aria-disabled') !== 'true',\n );\n if (options.length > 0) {\n const enumValues = options\n .map((o) => (o.getAttribute('data-value') ?? o.textContent ?? '').trim())\n .filter(Boolean);\n const oneOf = options.map((o) => ({\n const: (o.getAttribute('data-value') ?? o.textContent ?? '').trim(),\n title: (o.textContent ?? '').trim(),\n }));\n return { type: 'string', enum: enumValues, oneOf };\n }\n }\n }\n return { type: 'string' };\n }\n\n case 'textbox':\n case 'searchbox':\n case 'radio':\n default:\n return { type: 'string' };\n }\n}\n\nfunction getRadioLabelText(radio: HTMLInputElement): string {\n // 1. Wrapping label\n const parent = radio.closest('label');\n if (parent) {\n const clone = parent.cloneNode(true) as HTMLLabelElement;\n clone.querySelectorAll('input, select, textarea, button').forEach((el) => el.remove());\n const text = clone.textContent?.trim() ?? '';\n if (text) return text;\n }\n // 2. Label pointing to radio by id\n if (radio.id) {\n const label = document.querySelector<HTMLLabelElement>(`label[for=\"${CSS.escape(radio.id)}\"]`);\n if (label) {\n const text = label.textContent?.trim() ?? '';\n if (text) return text;\n }\n }\n return '';\n}\n", "/**\n * analyzer.ts \u2014 Infer tool name, description, and JSON Schema from form DOM\n */\n\nimport { JsonSchema, JsonSchemaProperty, inputTypeToSchema, collectRadioEnum, collectRadioOneOf, collectCheckboxEnum, ARIA_ROLES_TO_SCAN, AriaRole, ariaRoleToSchema } from './schema.js';\nimport { FormOverride } from './config.js';\n\nexport interface ToolAnnotations {\n readOnlyHint?: boolean;\n destructiveHint?: boolean;\n idempotentHint?: boolean;\n openWorldHint?: boolean;\n}\n\nexport interface ToolMetadata {\n name: string;\n description: string;\n inputSchema: JsonSchema;\n annotations?: ToolAnnotations;\n /** Key \u2192 DOM element for fields not addressable by name (id-keyed or ARIA-role controls). */\n fieldElements?: Map<string, Element>;\n}\n\n// Track form index for fallback naming\nlet formIndex = 0;\n\n/** Reset form index counter (useful in tests) */\nexport function resetFormIndex(): void {\n formIndex = 0;\n}\n\n/** Derive ToolMetadata from a <form> element */\nexport function analyzeForm(form: HTMLFormElement, override?: FormOverride): ToolMetadata {\n const name = override?.name ?? inferToolName(form);\n const description = override?.description ?? inferToolDescription(form);\n const { schema: inputSchema, fieldElements } = buildSchema(form);\n const annotations = inferAnnotations(form);\n\n return { name, description, inputSchema, annotations, fieldElements };\n}\n\n// ---------------------------------------------------------------------------\n// Tool name inference\n// ---------------------------------------------------------------------------\n\nfunction inferToolName(form: HTMLFormElement): string {\n // 1. Native toolname attribute (spec)\n const nativeName = form.getAttribute('toolname');\n if (nativeName) return sanitizeName(nativeName);\n\n // 2. Explicit data attribute\n const explicit = form.dataset['webmcpName'];\n if (explicit) return sanitizeName(explicit);\n\n // 2. Submit button text\n const submitText = getSubmitButtonText(form);\n if (submitText) return sanitizeName(submitText);\n\n // 3. Nearest heading above the form\n const heading = getNearestHeadingText(form);\n if (heading) return sanitizeName(heading);\n\n // 4. Form id or name attribute\n if (form.id) return sanitizeName(form.id);\n if (form.name) return sanitizeName(form.name);\n\n // 5. Form action URL path segment\n if (form.action) {\n const segment = getLastPathSegment(form.action);\n if (segment) return sanitizeName(segment);\n }\n\n // 6. Fallback\n return `form_${++formIndex}`;\n}\n\nfunction sanitizeName(raw: string): string {\n return raw\n .toLowerCase()\n .trim()\n .replace(/[^a-z0-9]+/g, '_')\n .replace(/^_+|_+$/g, '')\n .slice(0, 64) || 'form';\n}\n\nfunction getSubmitButtonText(form: HTMLFormElement): string {\n const buttons = [\n ...Array.from(form.querySelectorAll<HTMLButtonElement>('button[type=\"submit\"], button:not([type])')),\n ...Array.from(form.querySelectorAll<HTMLInputElement>('input[type=\"submit\"]')),\n ];\n\n for (const btn of buttons) {\n const text =\n btn instanceof HTMLInputElement\n ? btn.value.trim()\n : btn.textContent?.trim() ?? '';\n if (text && text.length > 0 && text.length < 80) return text;\n }\n return '';\n}\n\nfunction getNearestHeadingText(form: HTMLFormElement): string {\n // Walk up the DOM looking for a preceding sibling or parent heading\n let node: Element | null = form;\n while (node) {\n // Check preceding siblings\n let sibling = node.previousElementSibling;\n while (sibling) {\n if (/^H[1-3]$/i.test(sibling.tagName)) {\n const text = sibling.textContent?.trim() ?? '';\n if (text) return text;\n }\n sibling = sibling.previousElementSibling;\n }\n node = node.parentElement;\n // Stop at body\n if (!node || node === document.body) break;\n }\n return '';\n}\n\nfunction getLastPathSegment(url: string): string {\n try {\n const parsed = new URL(url, window.location.href);\n const segments = parsed.pathname.split('/').filter(Boolean);\n return segments[segments.length - 1] ?? '';\n } catch {\n return '';\n }\n}\n\n// ---------------------------------------------------------------------------\n// Tool description inference\n// ---------------------------------------------------------------------------\n\nfunction inferToolDescription(form: HTMLFormElement): string {\n // 1. Native tooldescription attribute (spec)\n const nativeDesc = form.getAttribute('tooldescription');\n if (nativeDesc) return nativeDesc.trim();\n\n // 2. Explicit data attribute\n const explicit = form.dataset['webmcpDescription'];\n if (explicit) return explicit.trim();\n\n // 2. <legend> inside the form\n const legend = form.querySelector('legend');\n if (legend?.textContent?.trim()) return legend.textContent.trim();\n\n // 3. aria-label on the form\n const ariaLabel = form.getAttribute('aria-label');\n if (ariaLabel?.trim()) return ariaLabel.trim();\n\n // 4. aria-describedby\n const describedById = form.getAttribute('aria-describedby');\n if (describedById) {\n const descEl = document.getElementById(describedById);\n if (descEl?.textContent?.trim()) return descEl.textContent.trim();\n }\n\n // 5. Combine nearest heading + page title\n const heading = getNearestHeadingText(form);\n const pageTitle = document.title?.trim();\n if (heading && pageTitle) return `${heading}: ${pageTitle}`;\n if (heading) return heading;\n if (pageTitle) return pageTitle;\n\n return 'Submit form';\n}\n\n// ---------------------------------------------------------------------------\n// Tool annotations inference\n// ---------------------------------------------------------------------------\n\nconst READONLY_BUTTON_PATTERNS = /^(search|find|look|filter|browse|view|show|check|preview|get|fetch|retrieve|load)\\b/i;\nconst DESTRUCTIVE_BUTTON_PATTERNS = /^(delete|remove|cancel|terminate|destroy|purge|revoke|unsubscribe|deactivate)\\b/i;\nconst DESTRUCTIVE_URL_PATTERNS = /\\/(delete|remove|cancel|destroy)\\b/i;\n\nfunction inferAnnotations(form: HTMLFormElement): ToolAnnotations {\n const annotations: ToolAnnotations = {};\n\n // Manual overrides via data-webmcp-* attributes take highest priority\n if (form.dataset['webmcpReadonly'] !== undefined) {\n annotations.readOnlyHint = form.dataset['webmcpReadonly'] !== 'false';\n }\n if (form.dataset['webmcpDestructive'] !== undefined) {\n annotations.destructiveHint = form.dataset['webmcpDestructive'] !== 'false';\n }\n if (form.dataset['webmcpIdempotent'] !== undefined) {\n annotations.idempotentHint = form.dataset['webmcpIdempotent'] !== 'false';\n }\n if (form.dataset['webmcpOpenworld'] !== undefined) {\n annotations.openWorldHint = form.dataset['webmcpOpenworld'] !== 'false';\n }\n\n // readOnlyHint: GET method or common read-action button text\n if (annotations.readOnlyHint === undefined) {\n const isGet = form.method.toLowerCase() === 'get';\n const submitText = getSubmitButtonText(form);\n const isReadLabel = submitText ? READONLY_BUTTON_PATTERNS.test(submitText.trim()) : false;\n if (isGet || isReadLabel) annotations.readOnlyHint = true;\n }\n\n // destructiveHint: common delete/cancel button text or URL path\n if (annotations.destructiveHint === undefined) {\n const submitText = getSubmitButtonText(form);\n const isDestructiveLabel = submitText ? DESTRUCTIVE_BUTTON_PATTERNS.test(submitText.trim()) : false;\n const isDestructiveUrl = form.action ? DESTRUCTIVE_URL_PATTERNS.test(form.action) : false;\n if (isDestructiveLabel || isDestructiveUrl) annotations.destructiveHint = true;\n }\n\n // idempotentHint: read-only forms and GET requests are naturally idempotent\n if (annotations.idempotentHint === undefined) {\n if (annotations.readOnlyHint === true || form.method.toLowerCase() === 'get') {\n annotations.idempotentHint = true;\n }\n }\n\n // openWorldHint: forms that write/mutate data interact with external entities\n if (annotations.openWorldHint === undefined) {\n annotations.openWorldHint = annotations.readOnlyHint !== true;\n }\n\n // Return empty object if no non-default hints were inferred (avoids polluting registrations)\n const hasNonDefault =\n annotations.readOnlyHint === true ||\n annotations.destructiveHint === true ||\n annotations.idempotentHint === true ||\n annotations.openWorldHint === false;\n\n return hasNonDefault ? annotations : {};\n}\n\n// ---------------------------------------------------------------------------\n// JSON Schema construction\n// ---------------------------------------------------------------------------\n\n/**\n * Returns the current DOM value of a control as a JSON Schema `default`, or\n * `undefined` if the control is empty/unchecked (no useful default to expose).\n * Number and range inputs are returned as numbers, not strings.\n */\nfunction extractDefaultValue(\n control: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement,\n): unknown {\n if (control instanceof HTMLInputElement) {\n const type = control.type.toLowerCase();\n if (type === 'checkbox') return control.checked ? true : undefined;\n if (type === 'radio') return undefined; // handled at the radio-group level\n if (type === 'number' || type === 'range') {\n return control.value !== '' ? parseFloat(control.value) : undefined;\n }\n return control.value !== '' ? control.value : undefined;\n }\n if (control instanceof HTMLTextAreaElement) {\n return control.value !== '' ? control.value : undefined;\n }\n if (control instanceof HTMLSelectElement) {\n if (control.multiple) {\n const selected = Array.from(control.options).filter((o) => o.selected).map((o) => o.value);\n return selected.length > 0 ? selected : undefined;\n }\n return control.value !== '' ? control.value : undefined;\n }\n return undefined;\n}\n\n/**\n * Recursively collect native form controls inside shadow roots.\n * querySelectorAll() does not pierce shadow DOM, so Web Components and\n * design-system elements that render inputs inside their shadow root are\n * invisible to the standard form scan. This helper walks every element,\n * enters its shadowRoot when present, and collects inputs/textareas/selects.\n */\nfunction collectShadowControls(\n root: Element | ShadowRoot,\n visited = new Set<Element | ShadowRoot>(),\n): Array<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement> {\n if (visited.has(root)) return [];\n visited.add(root);\n const results: Array<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement> = [];\n for (const el of Array.from(root.querySelectorAll('*'))) {\n if (el.shadowRoot) {\n const found = Array.from(\n el.shadowRoot.querySelectorAll<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>(\n 'input, textarea, select',\n ),\n );\n if (found.length > 0) {\n console.log(`[auto-webmcp] shadow: found ${found.length} control(s) in ${el.tagName.toLowerCase()} shadow root:`, found.map(f => `${f.tagName.toLowerCase()}[type=${f.type ?? '?'}][name=\"${(f as HTMLInputElement).name}\"][id=\"${f.id}\"]`));\n }\n results.push(...found, ...collectShadowControls(el.shadowRoot, visited));\n }\n }\n return results;\n}\n\n/**\n * Collect native controls associated with a form.\n * Uses form.elements to include controls outside the form subtree that point\n * to this form via the HTML `form` attribute.\n */\nfunction collectFormAssociatedControls(\n form: HTMLFormElement,\n): Array<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement> {\n const controls = Array.from(form.elements).filter(\n (el): el is HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement =>\n el instanceof HTMLInputElement ||\n el instanceof HTMLTextAreaElement ||\n el instanceof HTMLSelectElement,\n );\n\n const seen = new Set<Element>(controls);\n for (const shadowControl of collectShadowControls(form)) {\n if (!seen.has(shadowControl)) {\n controls.push(shadowControl);\n seen.add(shadowControl);\n }\n }\n\n return controls;\n}\n\nfunction buildSchema(form: HTMLFormElement): { schema: JsonSchema; fieldElements: Map<string, Element> } {\n const properties: Record<string, JsonSchemaProperty> = {};\n const required: string[] = [];\n const fieldElements = new Map<string, Element>();\n\n // Track which radio/checkbox group names we've already processed\n const processedRadioGroups = new Set<string>();\n const processedCheckboxGroups = new Set<string>();\n\n const controls = collectFormAssociatedControls(form);\n\n for (const control of controls) {\n const name = control.name;\n const fieldKey = name || resolveNativeControlFallbackKey(control);\n if (!fieldKey) continue;\n\n // Skip already-processed radio groups\n if (control instanceof HTMLInputElement && control.type === 'radio') {\n if (processedRadioGroups.has(fieldKey)) continue;\n processedRadioGroups.add(fieldKey);\n }\n\n // Skip already-processed checkbox groups\n if (control instanceof HTMLInputElement && control.type === 'checkbox') {\n if (processedCheckboxGroups.has(fieldKey)) continue;\n processedCheckboxGroups.add(fieldKey);\n }\n\n const schemaProp = inputTypeToSchema(control);\n if (!schemaProp) continue; // skipped types (hidden, password, file, etc.)\n if (!isControlVisible(control)) continue; // display:none, aria-hidden, disabled fieldset\n\n // Enrich with title and description\n schemaProp.title = inferFieldTitle(control);\n const desc = inferFieldDescription(control);\n if (desc) schemaProp.description = desc;\n\n // Attach current DOM value as JSON Schema 'default' so agents know the pre-filled state\n const defaultVal = extractDefaultValue(control);\n if (defaultVal !== undefined) schemaProp.default = defaultVal;\n\n // For radio groups, add enum, oneOf, and default (currently checked value)\n if (control instanceof HTMLInputElement && control.type === 'radio') {\n schemaProp.enum = collectRadioEnum(form, fieldKey);\n const radioOneOf = collectRadioOneOf(form, fieldKey);\n if (radioOneOf.length > 0) schemaProp.oneOf = radioOneOf;\n const checkedRadio = Array.from(form.elements).find(\n (el): el is HTMLInputElement =>\n el instanceof HTMLInputElement &&\n el.type === 'radio' &&\n el.name === fieldKey &&\n el.checked,\n );\n if (checkedRadio?.value) schemaProp.default = checkedRadio.value;\n }\n\n // For checkbox groups (multiple checkboxes with same name), upgrade to array schema\n if (control instanceof HTMLInputElement && control.type === 'checkbox') {\n const checkboxValues = collectCheckboxEnum(form, fieldKey);\n if (checkboxValues.length > 1) {\n const arrayProp: JsonSchemaProperty = {\n type: 'array',\n items: { type: 'string', enum: checkboxValues },\n title: schemaProp.title,\n };\n if (schemaProp.description) arrayProp.description = schemaProp.description;\n const checkedBoxes = Array.from(form.elements)\n .filter(\n (el): el is HTMLInputElement =>\n el instanceof HTMLInputElement &&\n el.type === 'checkbox' &&\n el.name === fieldKey &&\n el.checked,\n )\n .map((b) => b.value);\n if (checkedBoxes.length > 0) arrayProp.default = checkedBoxes;\n properties[fieldKey] = arrayProp;\n if (control.required) required.push(fieldKey);\n continue;\n }\n }\n\n properties[fieldKey] = schemaProp;\n\n // Track id-keyed or aria-label-keyed fields for the interceptor\n if (!name) {\n fieldElements.set(fieldKey, control);\n }\n\n // Mark as required \u2014 check both the native attribute AND shadow host chain.\n // Web Component wrappers (e.g. lightning-input-field[required]) set required on the\n // host element, not on the inner <input>, so control.required may be false even when\n // the field is logically required.\n let isRequired = control.required;\n if (!isRequired) {\n let hostNode: Element = control;\n while (true) {\n const root = hostNode.getRootNode();\n if (!(root instanceof ShadowRoot)) break;\n const host = root.host;\n if (host.hasAttribute('required') || host.getAttribute('aria-required') === 'true') {\n isRequired = true;\n break;\n }\n hostNode = host;\n }\n }\n if (isRequired) required.push(fieldKey);\n }\n\n // ARIA role-based controls (custom components not using native inputs)\n const ariaControls = collectAriaControls(form);\n const processedAriaRadioGroups = new Set<string>();\n\n for (const { el, role, key, enumValues, enumOneOf } of ariaControls) {\n if (properties[key]) continue; // already covered by a native control\n\n if (role === 'radio') {\n if (processedAriaRadioGroups.has(key)) continue;\n processedAriaRadioGroups.add(key);\n }\n\n const schemaProp = ariaRoleToSchema(el, role);\n\n // Apply pre-computed enum for grouped ARIA radiogroups (role=\"radiogroup\" ancestor)\n if (enumValues && enumValues.length > 0) {\n schemaProp.enum = enumValues;\n if (enumOneOf && enumOneOf.length > 0) schemaProp.oneOf = enumOneOf;\n }\n\n schemaProp.title = inferAriaFieldTitle(el);\n const desc = inferAriaFieldDescription(el);\n if (desc) schemaProp.description = desc;\n\n properties[key] = schemaProp;\n fieldElements.set(key, el);\n\n if (el.getAttribute('aria-required') === 'true') {\n required.push(key);\n }\n }\n\n return { schema: { '$schema': 'https://json-schema.org/draft/2020-12/schema', type: 'object', properties, required }, fieldElements };\n}\n\n/** Matches auto-generated IDs that frameworks (React, etc.) assign and that carry no semantic meaning. */\nconst AUTO_GENERATED_ID_RE = /^_r_[0-9a-z]+_$|^:[a-z0-9]+:$/i;\n\n/** Derive a schema key for a native control that lacks a name attribute */\nfunction resolveNativeControlFallbackKey(\n control: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement,\n): string | null {\n const el = control as HTMLElement;\n if (el.dataset['webmcpName']) return sanitizeName(el.dataset['webmcpName']!);\n // Skip auto-generated framework IDs (React: _r_1_, :r0:) \u2014 they are not meaningful.\n // Prefer aria-label or placeholder which carry semantic meaning.\n if (control.id && !AUTO_GENERATED_ID_RE.test(control.id)) return sanitizeName(control.id);\n const label = control.getAttribute('aria-label');\n if (label) return sanitizeName(label);\n // Fallback: placeholder text \u2014 common for minimalist forms (e.g. Ghost newsletter)\n // that have no name/id/aria-label on their inputs.\n if (\n (control instanceof HTMLInputElement || control instanceof HTMLTextAreaElement) &&\n control.placeholder?.trim()\n ) {\n return sanitizeName(control.placeholder.trim());\n }\n // Shadow host chain: Web Component wrappers (e.g. Salesforce lightning-input-field,\n // lightning-input) carry the semantic field identifier on the host element, not on the\n // inner <input>. Walk up through shadow roots to find field-name / label / name / aria-label.\n const hostKey = resolveShadowHostKey(control);\n if (hostKey) return hostKey;\n // Final fallback: input type for typed inputs without any text identifier.\n if (control instanceof HTMLInputElement && control.type !== 'text') {\n return control.type;\n }\n return null;\n}\n\n/**\n * Walk up the shadow root host chain from a shadow DOM element and return the first\n * meaningful field identifier found on a host element.\n * Checks (in order): field-name, label, aria-label, name attributes.\n * Covers Salesforce LWC (lightning-input-field[field-name]), generic Web Components,\n * and any design system that wraps native inputs in custom elements.\n */\nfunction resolveShadowHostKey(el: Element): string | null {\n let node: Element = el;\n while (true) {\n const root = node.getRootNode();\n if (!(root instanceof ShadowRoot)) break;\n const host = root.host;\n const fieldName = host.getAttribute('field-name');\n if (fieldName) { console.log('[auto-webmcp] shadow host key: field-name=', fieldName); return sanitizeName(fieldName); }\n const hostLabel = host.getAttribute('label') || host.getAttribute('aria-label');\n if (hostLabel) { console.log('[auto-webmcp] shadow host key: label=', hostLabel); return sanitizeName(hostLabel); }\n const hostName = host.getAttribute('name');\n if (hostName) { console.log('[auto-webmcp] shadow host key: name=', hostName); return sanitizeName(hostName); }\n node = host;\n }\n return null;\n}\n\n/** Key derivation for ARIA / contenteditable elements that are not native form controls. */\nfunction resolveAriaElementKey(el: Element): string | null {\n if ((el as HTMLElement).dataset['webmcpName']) return sanitizeName((el as HTMLElement).dataset['webmcpName']!);\n if (el.id && !AUTO_GENERATED_ID_RE.test(el.id)) return sanitizeName(el.id);\n const label = el.getAttribute('aria-label');\n if (label) return sanitizeName(label);\n const placeholder = el.getAttribute('placeholder');\n if (placeholder) return sanitizeName(placeholder);\n return null;\n}\n\ntype AriaControlEntry = {\n el: Element;\n role: AriaRole;\n key: string;\n enumValues?: string[];\n enumOneOf?: Array<{ const: string; title: string }>;\n};\n\n/** Collect ARIA-role-based interactive elements inside a form, excluding native inputs.\n * ARIA radio elements inside a role=\"radiogroup\" ancestor are collapsed into a single\n * enum field keyed on the group, matching native radio group behaviour. */\nfunction collectAriaControls(form: HTMLFormElement): Array<AriaControlEntry> {\n const selector = ARIA_ROLES_TO_SCAN.map((r) => `[role=\"${r}\"]`).join(', ');\n const rawResults: Array<{ el: Element; role: AriaRole; key: string }> = [];\n\n for (const el of Array.from(form.querySelectorAll(selector))) {\n // Skip native inputs \u2014 already handled above\n if (\n el instanceof HTMLInputElement ||\n el instanceof HTMLTextAreaElement ||\n el instanceof HTMLSelectElement\n ) continue;\n\n // Skip hidden elements\n if (el.getAttribute('aria-hidden') === 'true' || (el as HTMLElement).hidden) continue;\n\n const role = el.getAttribute('role') as AriaRole;\n const key = resolveAriaFieldKey(el);\n if (!key) continue;\n\n rawResults.push({ el, role, key });\n }\n\n // Group ARIA radios by their nearest role=\"radiogroup\" ancestor\n const radioEntries = rawResults.filter((e) => e.role === 'radio');\n const nonRadioEntries: AriaControlEntry[] = rawResults.filter((e) => e.role !== 'radio');\n\n const radioGroupMap = new Map<Element, Array<Element>>();\n const ungroupedRadios: AriaControlEntry[] = [];\n\n for (const entry of radioEntries) {\n const group = entry.el.closest('[role=\"radiogroup\"]');\n if (group) {\n if (!radioGroupMap.has(group)) radioGroupMap.set(group, []);\n radioGroupMap.get(group)!.push(entry.el);\n } else {\n ungroupedRadios.push(entry);\n }\n }\n\n const groupedEntries: AriaControlEntry[] = [];\n for (const [group, members] of radioGroupMap) {\n const groupKey = resolveAriaFieldKey(group);\n if (!groupKey) continue;\n const enumValues = members\n .map((el) => (el.getAttribute('data-value') ?? el.getAttribute('aria-label') ?? el.textContent ?? '').trim())\n .filter(Boolean);\n const enumOneOf = members\n .map((el) => {\n const val = (el.getAttribute('data-value') ?? el.getAttribute('aria-label') ?? el.textContent ?? '').trim();\n const title = (el.getAttribute('aria-label') ?? el.textContent ?? '').trim();\n return { const: val, title: title || val };\n })\n .filter((e) => e.const !== '');\n if (enumValues.length > 0) {\n groupedEntries.push({ el: group, role: 'radio', key: groupKey, enumValues, enumOneOf });\n }\n }\n\n return [...nonRadioEntries, ...groupedEntries, ...ungroupedRadios];\n}\n\n/** Derive a schema key from an ARIA element */\nfunction resolveAriaFieldKey(el: Element): string | null {\n const htmlEl = el as HTMLElement;\n if (htmlEl.dataset?.['webmcpName']) return sanitizeName(htmlEl.dataset['webmcpName']!);\n if (el.id) return sanitizeName(el.id);\n const label = el.getAttribute('aria-label');\n if (label) return sanitizeName(label);\n const labelledById = el.getAttribute('aria-labelledby');\n if (labelledById) {\n const text = document.getElementById(labelledById)?.textContent?.trim();\n if (text) return sanitizeName(text);\n }\n return null;\n}\n\nfunction inferAriaFieldTitle(el: Element): string {\n const htmlEl = el as HTMLElement;\n if (htmlEl.dataset?.['webmcpTitle']) return htmlEl.dataset['webmcpTitle']!;\n const label = el.getAttribute('aria-label');\n if (label) return label.trim();\n const labelledById = el.getAttribute('aria-labelledby');\n if (labelledById) {\n const text = document.getElementById(labelledById)?.textContent?.trim();\n if (text) return text;\n }\n if (el.id) return humanizeName(el.id);\n return '';\n}\n\nfunction inferAriaFieldDescription(el: Element): string {\n const nativeParamDesc = el.getAttribute('toolparamdescription');\n if (nativeParamDesc) return nativeParamDesc.trim();\n const htmlEl = el as HTMLElement;\n if (htmlEl.dataset?.['webmcpDescription']) return htmlEl.dataset['webmcpDescription']!;\n const ariaDesc = el.getAttribute('aria-description');\n if (ariaDesc) return ariaDesc;\n const describedById = el.getAttribute('aria-describedby');\n if (describedById) {\n const text = document.getElementById(describedById)?.textContent?.trim();\n if (text) return text;\n }\n const placeholder = el.getAttribute('placeholder') ?? (el as HTMLElement).dataset?.['placeholder'];\n if (placeholder) return placeholder.trim();\n return '';\n}\n\nfunction inferFieldTitle(\n control: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement,\n): string {\n // 1. data-webmcp-title\n if ('dataset' in control && (control as HTMLElement).dataset['webmcpTitle']) {\n return (control as HTMLElement).dataset['webmcpTitle']!;\n }\n\n // 2. Associated <label> text\n const labelText = getAssociatedLabelText(control);\n if (labelText) return labelText;\n\n // 3. name attribute (humanised)\n if (control.name) return humanizeName(control.name);\n\n // 4. id attribute (humanised)\n if (control.id) return humanizeName(control.id);\n\n // 5. placeholder text (last resort for inputs with no name/id/label)\n if (\n (control instanceof HTMLInputElement || control instanceof HTMLTextAreaElement) &&\n control.placeholder?.trim()\n ) {\n return control.placeholder.trim();\n }\n\n return '';\n}\n\nfunction inferFieldDescription(\n control: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement,\n): string {\n // 1. Native toolparamdescription attribute (spec)\n const nativeParamDesc = control.getAttribute('toolparamdescription');\n if (nativeParamDesc) return nativeParamDesc.trim();\n\n // 2. data-webmcp-description\n const el = control as HTMLElement;\n if (el.dataset['webmcpDescription']) return el.dataset['webmcpDescription']!;\n\n // 2. aria-description or aria-describedby\n const ariaDesc = control.getAttribute('aria-description');\n if (ariaDesc) return ariaDesc;\n\n const describedById = control.getAttribute('aria-describedby');\n if (describedById) {\n const descEl = document.getElementById(describedById);\n if (descEl?.textContent?.trim()) return descEl.textContent.trim();\n }\n\n // 3. placeholder (only as a last resort \u2014 can be noisy)\n if (control instanceof HTMLInputElement || control instanceof HTMLTextAreaElement) {\n const ph = control.placeholder?.trim();\n if (ph && ph.length > 0) return ph;\n }\n\n // 4. Associated label text (if title didn't use it, use it for description)\n return '';\n}\n\nfunction getAssociatedLabelText(\n control: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement,\n): string {\n // 1a. Labels collection (for/id association) \u2014 light DOM\n if (control.id) {\n const label = document.querySelector<HTMLLabelElement>(`label[for=\"${CSS.escape(control.id)}\"]`);\n if (label) {\n const text = labelTextWithoutNested(label);\n if (text) return text;\n }\n }\n\n // 1b. Same shadow root label search \u2014 Web Components render labels inside their own\n // shadow root so document.querySelector() cannot find them. Search the same root node.\n const ownRoot = control.getRootNode();\n if (ownRoot instanceof ShadowRoot) {\n if (control.id) {\n const shadowLabel = ownRoot.querySelector<HTMLLabelElement>(`label[for=\"${CSS.escape(control.id)}\"]`);\n if (shadowLabel) {\n const text = labelTextWithoutNested(shadowLabel);\n if (text) return text;\n }\n }\n // Also try: first <label> or element with slot=\"label\" in same shadow root\n const anyLabel = ownRoot.querySelector<HTMLLabelElement>('label');\n if (anyLabel) {\n const text = labelTextWithoutNested(anyLabel);\n if (text) return text;\n }\n }\n\n // 2. Wrapping <label> (works within the same shadow root via closest())\n const parent = control.closest('label');\n if (parent) {\n const text = labelTextWithoutNested(parent);\n if (text) return text;\n }\n\n // 3. label / aria-label on shadow host elements\n let node: Element = control;\n while (true) {\n const root = node.getRootNode();\n if (!(root instanceof ShadowRoot)) break;\n const host = root.host;\n const hostLabel = host.getAttribute('label') || host.getAttribute('aria-label');\n if (hostLabel) return hostLabel;\n node = host;\n }\n\n return '';\n}\n\nfunction labelTextWithoutNested(label: HTMLLabelElement): string {\n // Clone and remove any nested input/select/textarea before getting text\n const clone = label.cloneNode(true) as HTMLLabelElement;\n clone.querySelectorAll('input, select, textarea, button').forEach((el) => el.remove());\n return clone.textContent?.trim() ?? '';\n}\n\nfunction humanizeName(raw: string): string {\n return raw\n .replace(/[-_]/g, ' ')\n .replace(/([a-z])([A-Z])/g, '$1 $2')\n .trim()\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n}\n\n/**\n * Returns false if the control is currently not visible to the user:\n * display:none, visibility:hidden, aria-hidden ancestor, or inside a disabled fieldset.\n * Used to exclude conditional/hidden fields from the registered schema so agents\n * are not offered fields that are not currently applicable.\n *\n * Note: <input type=\"hidden\"> is excluded earlier by inputTypeToSchema returning null \u2014\n * this check never runs for hidden-type inputs.\n */\nfunction isControlVisible(el: HTMLElement): boolean {\n const style = window.getComputedStyle(el);\n if (style.display === 'none') return false;\n if (style.visibility === 'hidden') return false;\n // offsetParent is null for display:none (already caught) AND position:fixed (which IS visible)\n if (el.offsetParent === null && style.position !== 'fixed') return false;\n\n // Walk up ancestors checking for aria-hidden\n let node: Element | null = el;\n while (node && node !== document.body) {\n if (node.getAttribute('aria-hidden') === 'true') return false;\n node = node.parentElement;\n }\n\n // Disabled fieldset hides all its controls from agents\n if (el.closest('fieldset')?.disabled) return false;\n\n return true;\n}\n\n// ---------------------------------------------------------------------------\n// Orphan input group analysis (inputs not inside a <form> element)\n// ---------------------------------------------------------------------------\n\n/**\n * Derive ToolMetadata from a group of form controls that are NOT inside a <form>.\n * Used by discovery.ts's orphan-input scanner for pages like newsletter landing pages.\n */\nexport function analyzeOrphanInputGroup(\n container: Element,\n inputs: Array<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | HTMLElement>,\n submitBtn: HTMLButtonElement | HTMLInputElement | null,\n): ToolMetadata {\n const name = inferOrphanToolName(container, submitBtn);\n const description = inferOrphanToolDescription(container);\n const { schema: inputSchema, fieldElements } = buildSchemaFromInputs(inputs);\n const annotations = inferOrphanAnnotations(submitBtn);\n return { name, description, inputSchema, annotations, fieldElements };\n}\n\nfunction inferOrphanAnnotations(\n submitBtn: HTMLButtonElement | HTMLInputElement | null,\n): ToolAnnotations {\n const annotations: ToolAnnotations = {};\n const submitText =\n submitBtn instanceof HTMLInputElement\n ? submitBtn.value.trim()\n : (submitBtn?.textContent?.trim() ?? '');\n\n if (READONLY_BUTTON_PATTERNS.test(submitText)) {\n annotations.readOnlyHint = true;\n annotations.idempotentHint = true;\n }\n if (DESTRUCTIVE_BUTTON_PATTERNS.test(submitText)) {\n annotations.destructiveHint = true;\n }\n if (annotations.readOnlyHint !== true) {\n annotations.openWorldHint = true;\n }\n\n const hasNonDefault =\n annotations.readOnlyHint === true ||\n annotations.destructiveHint === true ||\n annotations.idempotentHint === true ||\n annotations.openWorldHint === false;\n\n return hasNonDefault ? annotations : {};\n}\n\nfunction inferOrphanToolName(\n container: Element,\n submitBtn: HTMLButtonElement | HTMLInputElement | null,\n): string {\n // 1. Submit button text\n if (submitBtn) {\n const text =\n submitBtn instanceof HTMLInputElement\n ? submitBtn.value.trim()\n : submitBtn.textContent?.trim() ?? '';\n if (text && text.length > 0 && text.length < 80) return sanitizeName(text);\n }\n\n // 2. Nearest heading within or above the container\n const heading = getNearestHeadingTextFrom(container);\n if (heading) return sanitizeName(heading);\n\n // 3. Page title\n const title = document.title?.trim();\n if (title) return sanitizeName(title);\n\n return `form_${++formIndex}`;\n}\n\nfunction inferOrphanToolDescription(container: Element): string {\n // Nearest heading within or above the container\n const heading = getNearestHeadingTextFrom(container);\n const pageTitle = document.title?.trim();\n if (heading && pageTitle && heading !== pageTitle) return `${heading} on ${pageTitle}`;\n if (heading) return heading;\n if (pageTitle) return pageTitle;\n return 'Submit form';\n}\n\n/**\n * Generic heading search starting from any Element (not just HTMLFormElement).\n * Walks up the DOM checking preceding siblings and parent elements.\n */\nfunction getNearestHeadingTextFrom(el: Element): string {\n // Also check headings inside the container itself\n const inner = el.querySelector('h1, h2, h3');\n if (inner?.textContent?.trim()) return inner.textContent.trim();\n\n let node: Element | null = el;\n while (node) {\n let sibling = node.previousElementSibling;\n while (sibling) {\n if (/^H[1-3]$/i.test(sibling.tagName)) {\n const text = sibling.textContent?.trim() ?? '';\n if (text) return text;\n }\n sibling = sibling.previousElementSibling;\n }\n node = node.parentElement;\n if (!node || node === document.body) break;\n }\n return '';\n}\n\n/**\n * Build a JSON Schema from an array of form controls (no <form> context needed).\n * Reuses the same field title/description inference as buildSchema().\n */\nfunction buildSchemaFromInputs(\n inputs: Array<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | HTMLElement>,\n): { schema: JsonSchema; fieldElements: Map<string, Element> } {\n const properties: Record<string, JsonSchemaProperty> = {};\n const required: string[] = [];\n const fieldElements = new Map<string, Element>();\n const processedRadioGroups = new Set<string>();\n const processedCheckboxGroups = new Set<string>();\n\n for (const control of inputs) {\n // ARIA / contenteditable elements (e.g. Twitter compose box, Notion editor)\n if (!(control instanceof HTMLInputElement) && !(control instanceof HTMLTextAreaElement) && !(control instanceof HTMLSelectElement)) {\n const fieldKey = resolveAriaElementKey(control);\n if (!fieldKey) continue;\n if (!isControlVisible(control)) continue;\n const prop: JsonSchemaProperty = { type: 'string' };\n prop.title = control.getAttribute('aria-label') ?? fieldKey;\n const desc = control.getAttribute('aria-description') ?? control.getAttribute('aria-describedby') ? null : null;\n if (desc) prop.description = desc;\n properties[fieldKey] = prop;\n fieldElements.set(fieldKey, control);\n required.push(fieldKey);\n continue;\n }\n\n const rawName = (control as HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement).name;\n // Sanitize the raw name (handles namespaced names like \"issue[title]\" \u2192 \"issue_title\")\n // so schema keys match the sanitized keys used in the execute handler.\n const fieldKey = (rawName ? sanitizeName(rawName) : null) || resolveNativeControlFallbackKey(control);\n if (!fieldKey) continue;\n\n if (control instanceof HTMLInputElement && control.type === 'radio') {\n if (processedRadioGroups.has(fieldKey)) continue;\n processedRadioGroups.add(fieldKey);\n }\n\n if (control instanceof HTMLInputElement && control.type === 'checkbox') {\n if (processedCheckboxGroups.has(fieldKey)) continue;\n processedCheckboxGroups.add(fieldKey);\n }\n\n const schemaProp = inputTypeToSchema(control);\n if (!schemaProp) continue; // skipped types (hidden, password, file, etc.)\n if (!isControlVisible(control)) continue; // display:none, aria-hidden, disabled fieldset\n\n schemaProp.title = inferFieldTitle(control);\n const desc = inferFieldDescription(control);\n if (desc) schemaProp.description = desc;\n\n // For checkbox groups, derive values from the inputs array (no form context here)\n if (control instanceof HTMLInputElement && control.type === 'checkbox') {\n const checkboxValues = inputs\n .filter((i): i is HTMLInputElement => i instanceof HTMLInputElement && i.type === 'checkbox' && i.name === fieldKey)\n .map((cb) => cb.value)\n .filter((v) => v !== '' && v !== 'on');\n if (checkboxValues.length > 1) {\n const arrayProp: JsonSchemaProperty = {\n type: 'array',\n items: { type: 'string', enum: checkboxValues },\n title: schemaProp.title,\n };\n if (schemaProp.description) arrayProp.description = schemaProp.description;\n properties[fieldKey] = arrayProp;\n if (control.required) required.push(fieldKey);\n continue;\n }\n }\n\n properties[fieldKey] = schemaProp;\n if (!rawName) fieldElements.set(fieldKey, control);\n if (control.required) required.push(fieldKey);\n }\n\n return { schema: { '$schema': 'https://json-schema.org/draft/2020-12/schema', type: 'object', properties, required }, fieldElements };\n}\n", "/**\n * registry.ts \u2014 Wrapper around navigator.modelContext WebMCP Imperative API\n */\n\nimport { ToolMetadata } from './analyzer.js';\n\n// ---------------------------------------------------------------------------\n// WebMCP type declarations (not yet in TypeScript DOM lib)\n// ---------------------------------------------------------------------------\n\nexport interface WebMCPTool {\n name: string;\n description: string;\n inputSchema: object;\n outputSchema?: object;\n annotations?: {\n readOnlyHint?: boolean;\n destructiveHint?: boolean;\n idempotentHint?: boolean;\n openWorldHint?: boolean;\n };\n execute: (params: Record<string, unknown>, client?: unknown) => Promise<unknown>;\n}\n\n/**\n * Describes the structured JSON object returned by every auto-webmcp execute handler.\n * Declared as outputSchema on tool registration so agents can parse results reliably.\n */\nconst EXECUTE_OUTPUT_SCHEMA: object = {\n type: 'object',\n properties: {\n status: {\n type: 'string',\n enum: ['success', 'partial', 'error', 'awaiting_user_action', 'timed_out', 'blocked_invalid'],\n description: 'Outcome of the form execution.',\n },\n filled_fields: {\n type: 'object',\n description: 'Field name to submitted value map.',\n },\n skipped_fields: {\n type: 'array',\n items: { type: 'string' },\n description: 'Fields the agent provided but that could not be filled.',\n },\n missing_required: {\n type: 'array',\n items: { type: 'string' },\n description: 'Required fields not supplied by the agent.',\n },\n validation_errors: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n field: { type: 'string' },\n constraint: { type: 'string', description: 'HTML ValidityState key that failed.' },\n message: { type: 'string' },\n },\n required: ['field', 'constraint', 'message'],\n },\n description: 'Per-field HTML5 validation failures (present when status is blocked_invalid).',\n },\n existing_values: {\n type: 'object',\n description: 'Field values present in the form before the agent filled it.',\n },\n warnings: {\n type: 'array',\n items: { type: 'object' },\n description: 'Non-fatal fill warnings (alias_resolved, clamped, not_filled, etc.).',\n },\n },\n required: ['status', 'filled_fields', 'skipped_fields', 'missing_required', 'warnings'],\n};\n\ninterface ModelContextRegisterOptions {\n signal?: AbortSignal;\n}\n\ndeclare global {\n interface Navigator {\n modelContext?: {\n registerTool(tool: WebMCPTool, options?: ModelContextRegisterOptions): Promise<void> | void;\n unregisterTool?(name: string): Promise<void> | void;\n };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Registry\n// ---------------------------------------------------------------------------\n\n/** Tracks registered tools: form element \u2192 tool name */\nconst registeredTools = new Map<HTMLFormElement, string>();\n/** Tracks abort controllers for registrations, enabling signal-based unregister */\nconst registrationControllers = new Map<HTMLFormElement, AbortController>();\n\n/** True if the browser supports navigator.modelContext */\nexport function isWebMCPSupported(): boolean {\n return typeof navigator !== 'undefined' && typeof navigator.modelContext !== 'undefined';\n}\n\n/**\n * Register a form as a WebMCP tool.\n * Silently no-ops if WebMCP is not supported.\n */\nexport async function registerFormTool(\n form: HTMLFormElement,\n metadata: ToolMetadata,\n execute: (params: Record<string, unknown>, client?: unknown) => Promise<unknown>,\n): Promise<void> {\n if (!isWebMCPSupported()) return;\n\n // Unregister any previously-registered tool for this same form element\n const existing = registeredTools.get(form);\n if (existing) {\n await unregisterFormTool(form);\n }\n\n const toolDef: WebMCPTool = {\n name: metadata.name,\n description: metadata.description,\n inputSchema: metadata.inputSchema,\n outputSchema: EXECUTE_OUTPUT_SCHEMA,\n execute,\n };\n if (metadata.annotations && Object.keys(metadata.annotations).length > 0) {\n toolDef.annotations = metadata.annotations;\n }\n\n const controller = new AbortController();\n registrationControllers.set(form, controller);\n\n try {\n await navigator.modelContext!.registerTool(toolDef, { signal: controller.signal });\n } catch {\n // Chrome may hold a stale registration from a previous page load.\n // Unregister by name and retry once.\n try {\n await navigator.modelContext!.unregisterTool?.(metadata.name);\n await navigator.modelContext!.registerTool(toolDef, { signal: controller.signal });\n } catch {\n // Give up Chrome registration \u2014 local handlers and form:registered still work.\n }\n }\n\n registeredTools.set(form, metadata.name);\n}\n\n/**\n * Unregister the WebMCP tool associated with a form element.\n * Silently no-ops if not registered or WebMCP not supported.\n */\nexport async function unregisterFormTool(form: HTMLFormElement): Promise<void> {\n if (!isWebMCPSupported()) return;\n\n const name = registeredTools.get(form);\n if (!name) return;\n\n const controller = registrationControllers.get(form);\n if (controller) {\n controller.abort();\n registrationControllers.delete(form);\n }\n\n try {\n await navigator.modelContext!.unregisterTool?.(name);\n } catch {\n // Tool may have already been removed \u2014 ignore\n }\n\n registeredTools.delete(form);\n}\n\n/** Get the registered tool name for a form, if any */\nexport function getRegisteredToolName(form: HTMLFormElement): string | undefined {\n return registeredTools.get(form);\n}\n\n/** Return a snapshot of all currently registered form\u2192name pairs */\nexport function getAllRegisteredTools(): Array<{ form: HTMLFormElement; name: string }> {\n return Array.from(registeredTools.entries()).map(([form, name]) => ({ form, name }));\n}\n\n/** Unregister all tools (e.g. on teardown) */\nexport async function unregisterAll(): Promise<void> {\n const entries = Array.from(registeredTools.entries());\n await Promise.all(entries.map(([form]) => unregisterFormTool(form)));\n}\n", "/**\n * interceptor.ts \u2014 Form submit interception for agent-invoked submissions\n *\n * WebMCP's `execute` callback receives form parameters and is expected to\n * return a result. This module bridges the gap: it fills form fields with\n * the agent-supplied values, submits the form, and resolves the execute\n * promise with a structured response.\n */\n\nimport { ResolvedConfig } from './config.js';\nimport type { ToolMetadata } from './analyzer.js';\n\n// ---------------------------------------------------------------------------\n// Extended SubmitEvent types (WebMCP additions)\n// ---------------------------------------------------------------------------\n\ndeclare global {\n interface SubmitEvent {\n /** True when the form was submitted by an AI agent via WebMCP */\n agentInvoked?: boolean;\n /** Call to return a structured result to the agent */\n respondWith?: (promise: Promise<unknown>) => void;\n }\n}\n\nexport interface ExecuteResult {\n content: Array<{ type: 'text'; text: string }>;\n}\n\nexport interface FillWarning {\n field: string;\n type:\n | 'clamped'\n | 'not_filled'\n | 'missing_required'\n | 'type_mismatch'\n | 'alias_resolved'\n | 'blocked_submit'\n | 'timeout';\n message: string;\n original?: unknown;\n actual?: unknown;\n}\n\nexport interface ValidationError {\n field: string;\n /** HTML ValidityState key: valueMissing, typeMismatch, patternMismatch, tooLong, tooShort, rangeUnderflow, rangeOverflow, stepMismatch, customError, badInput */\n constraint: string;\n message: string;\n}\n\nexport interface StructuredExecuteData {\n status:\n | 'success'\n | 'partial'\n | 'error'\n | 'awaiting_user_action'\n | 'timed_out'\n | 'blocked_invalid';\n filled_fields: Record<string, unknown>;\n skipped_fields: string[];\n missing_required: string[];\n warnings: FillWarning[];\n /** Structured per-field validation errors (populated on blocked_invalid status). */\n validation_errors?: ValidationError[];\n /** Field values captured from the form before the agent filled it. */\n existing_values?: Record<string, unknown>;\n}\n\ninterface ModelContextClientLike {\n requestUserInteraction?: <T>(callback: () => Promise<T>) => Promise<T>;\n}\n\ntype Resolver = (result: ExecuteResult) => void;\ntype Rejecter = (error: Error) => void;\n\n/** Per-form pending execute promises */\nconst pendingExecutions = new WeakMap<\n HTMLFormElement,\n { resolve: Resolver; reject: Rejecter; timeoutId?: ReturnType<typeof setTimeout> }\n>();\n\n/** Per-form last-used params (for serializing id-keyed fields not in FormData) */\nconst lastParams = new WeakMap<HTMLFormElement, Record<string, unknown>>();\n\n/** Per-form field element map (key \u2192 DOM element for non-name-addressable fields) */\nconst formFieldElements = new WeakMap<HTMLFormElement, Map<string, Element>>();\n\n/** Per-form required fields that the agent did not supply (populated before submit, consumed in interceptor) */\nconst pendingWarnings = new WeakMap<HTMLFormElement, string[]>();\n\n/** Per-form fill warnings (invalid/out-of-range values detected during field filling) */\nconst pendingFillWarnings = new WeakMap<HTMLFormElement, FillWarning[]>();\n\n/**\n * Per-form snapshot of field values captured immediately after fillFormFields()\n * completes. Used by serializeFormData() so that id-keyed and ARIA-keyed fields\n * are serialized from the snapshot rather than re-querying a potentially stale\n * DOM (e.g. after a React reconciliation cycle resets field values).\n */\nconst lastFilledSnapshot = new WeakMap<HTMLFormElement, Record<string, unknown>>();\n\n/**\n * Per-form snapshot of field values captured BEFORE the agent fills the form.\n * Returned in execute results so agents know what was already in the form.\n */\nconst preFillValues = new WeakMap<HTMLFormElement, Record<string, unknown>>();\n\n// React-compatible native prototype setters (retrieved once at module init).\n// Using these bypasses React's controlled-input tracking so onChange fires correctly.\nconst _inputValueSetter: ((v: string) => void) | undefined =\n Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')?.set;\nconst _textareaValueSetter: ((v: string) => void) | undefined =\n Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, 'value')?.set;\nconst _checkedSetter: ((v: boolean) => void) | undefined =\n Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'checked')?.set;\n\nfunction normalizeAliasKey(raw: string): string {\n return raw.toLowerCase().replace(/[^a-z0-9]+/g, '');\n}\n\nfunction addAlias(\n index: Map<string, Set<string>>,\n alias: string | null | undefined,\n schemaKey: string,\n): void {\n if (!alias) return;\n const normalized = normalizeAliasKey(alias);\n if (!normalized) return;\n if (!index.has(normalized)) index.set(normalized, new Set<string>());\n index.get(normalized)!.add(schemaKey);\n}\n\nfunction buildAliasIndex(\n form: HTMLFormElement,\n metadata: ToolMetadata | undefined,\n): Map<string, Set<string>> {\n const index = new Map<string, Set<string>>();\n const properties = metadata?.inputSchema?.properties ?? {};\n\n for (const [schemaKey, prop] of Object.entries(properties)) {\n addAlias(index, schemaKey, schemaKey);\n addAlias(index, schemaKey.replace(/_/g, ' '), schemaKey);\n addAlias(index, prop.title, schemaKey);\n\n const nativeEl = findNativeField(form, schemaKey);\n const mappedEl = metadata?.fieldElements?.get(schemaKey);\n const el = nativeEl ?? mappedEl ?? null;\n if (!el) continue;\n const htmlEl = el as HTMLElement;\n addAlias(index, htmlEl.getAttribute('id'), schemaKey);\n addAlias(index, htmlEl.getAttribute('name'), schemaKey);\n addAlias(index, htmlEl.getAttribute('aria-label'), schemaKey);\n addAlias(index, htmlEl.getAttribute('placeholder'), schemaKey);\n if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement) {\n for (const label of Array.from(el.labels ?? [])) {\n addAlias(index, label.textContent?.trim(), schemaKey);\n }\n }\n }\n return index;\n}\n\nfunction resolveParamsForSchema(\n form: HTMLFormElement,\n params: Record<string, unknown>,\n metadata: ToolMetadata | undefined,\n config: ResolvedConfig,\n): { resolved: Record<string, unknown>; warnings: FillWarning[] } {\n const resolved: Record<string, unknown> = {};\n const warnings: FillWarning[] = [];\n const properties = metadata?.inputSchema?.properties ?? {};\n const aliasEnabled = config.paramBinding.enableAliasResolution;\n\n for (const [key, value] of Object.entries(params)) {\n if (key in properties) resolved[key] = value;\n }\n if (!aliasEnabled) return { resolved, warnings };\n\n const aliasIndex = buildAliasIndex(form, metadata);\n for (const [rawKey, value] of Object.entries(params)) {\n if (rawKey in properties) continue;\n const candidates = aliasIndex.get(normalizeAliasKey(rawKey));\n if (!candidates || candidates.size !== 1) continue;\n const target = Array.from(candidates)[0];\n if (!target || target in resolved) continue;\n resolved[target] = value;\n warnings.push({\n field: target,\n type: 'alias_resolved',\n original: rawKey,\n message: `resolved \"${rawKey}\" to schema field \"${target}\"`,\n });\n }\n return { resolved, warnings };\n}\n\nfunction collectInvalidFieldWarnings(form: HTMLFormElement): FillWarning[] {\n const warnings: FillWarning[] = [];\n const controls = Array.from(form.elements).filter(\n (el): el is HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement =>\n el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement,\n );\n for (const control of controls) {\n if (!control.willValidate) continue;\n if (control.checkValidity()) continue;\n const field = control.name || control.id || control.getAttribute('aria-label') || 'unknown_field';\n warnings.push({\n field,\n type: 'blocked_submit',\n message: control.validationMessage || `field \"${field}\" failed validation`,\n });\n }\n return warnings;\n}\n\n/**\n * Capture all current form field values before the agent fills them.\n * Uses FormData for named controls and falls back to direct property reads\n * for elements not included in FormData (e.g. unchecked checkboxes).\n */\nfunction captureCurrentValues(form: HTMLFormElement): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n try {\n const data = new FormData(form);\n for (const [key, val] of data.entries()) {\n if (result[key] !== undefined) {\n const existing = result[key];\n result[key] = Array.isArray(existing) ? [...existing, val] : [existing, val];\n } else {\n result[key] = val;\n }\n }\n } catch {\n // FormData constructor can throw for detached forms \u2014 return what we have\n }\n return result;\n}\n\n/**\n * Collect structured validation errors for all invalid form controls.\n * Returns richer information than collectInvalidFieldWarnings by including\n * the specific HTML ValidityState constraint that failed.\n */\nfunction collectValidationErrors(form: HTMLFormElement): ValidationError[] {\n const errors: ValidationError[] = [];\n for (const control of Array.from(form.elements)) {\n if (\n !(control instanceof HTMLInputElement) &&\n !(control instanceof HTMLTextAreaElement) &&\n !(control instanceof HTMLSelectElement)\n ) continue;\n if (!control.willValidate || control.checkValidity()) continue;\n const field = control.name || control.id || control.getAttribute('aria-label') || 'unknown_field';\n const v = control.validity;\n const constraint = v.valueMissing ? 'valueMissing'\n : v.typeMismatch ? 'typeMismatch'\n : v.patternMismatch ? 'patternMismatch'\n : v.tooLong ? 'tooLong'\n : v.tooShort ? 'tooShort'\n : v.rangeUnderflow ? 'rangeUnderflow'\n : v.rangeOverflow ? 'rangeOverflow'\n : v.stepMismatch ? 'stepMismatch'\n : v.customError ? 'customError'\n : 'badInput';\n errors.push({ field, constraint, message: control.validationMessage || `field \"${field}\" failed validation` });\n }\n return errors;\n}\n\n/**\n * Build an `execute` function for a form tool.\n *\n * When the agent calls execute(params):\n * 1. Fills form fields with the supplied params\n * 2. Fires a submit event (or auto-submits if configured)\n * 3. Resolves with structured form data once submitted\n */\nexport function buildExecuteHandler(\n form: HTMLFormElement,\n config: ResolvedConfig,\n toolName: string,\n metadata?: ToolMetadata,\n): (params: Record<string, unknown>, client?: unknown) => Promise<ExecuteResult> {\n // Store field element map for this form\n if (metadata?.fieldElements) {\n formFieldElements.set(form, metadata.fieldElements);\n }\n\n // Attach submit/reset listeners once per form\n attachSubmitInterceptor(form, toolName);\n\n return async (params: Record<string, unknown>, client?: unknown): Promise<ExecuteResult> => {\n const modelContextClient = client as ModelContextClientLike | undefined;\n if (\n config.autoSubmit &&\n metadata?.annotations?.destructiveHint === true &&\n typeof modelContextClient?.requestUserInteraction === 'function'\n ) {\n const approved = await modelContextClient.requestUserInteraction(async () => {\n return new Promise<boolean>((resolve) => {\n const ok = window.confirm(`Agent requested a destructive action via \"${toolName}\". Continue?`);\n resolve(ok);\n });\n });\n if (!approved) {\n window.dispatchEvent(new CustomEvent('toolcancel', { detail: { toolName } }));\n return { content: [{ type: 'text', text: `Cancelled \"${toolName}\" by user.` }] };\n }\n }\n\n pendingFillWarnings.set(form, []);\n pendingWarnings.delete(form);\n\n // Snapshot existing field values before filling so agents can see prior state.\n const existingSnapshot = captureCurrentValues(form);\n preFillValues.set(form, existingSnapshot);\n\n const { resolved: resolvedParams, warnings: aliasWarnings } = resolveParamsForSchema(\n form,\n params,\n metadata,\n config,\n );\n if (aliasWarnings.length > 0) {\n pendingFillWarnings.set(form, [...(pendingFillWarnings.get(form) ?? []), ...aliasWarnings]);\n }\n\n // If preserveExisting is enabled, skip filling fields that already have a non-empty value.\n let paramsToFill = resolvedParams;\n if (config.preserveExisting) {\n const preserved: FillWarning[] = [];\n paramsToFill = Object.fromEntries(\n Object.entries(resolvedParams).filter(([key]) => {\n const current = existingSnapshot[key];\n const hasValue = current !== undefined && current !== '' && current !== null;\n if (hasValue) {\n preserved.push({\n field: key,\n type: 'not_filled',\n message: `field \"${key}\" already has a value and preserveExisting is enabled`,\n });\n }\n return !hasValue;\n }),\n );\n if (preserved.length > 0) {\n pendingFillWarnings.set(form, [...(pendingFillWarnings.get(form) ?? []), ...preserved]);\n }\n }\n\n fillFormFields(form, paramsToFill);\n\n // Compute missing required fields now so they are available when the\n // form submits, regardless of whether autoSubmit is enabled.\n const missingNow = getMissingRequired(metadata, resolvedParams);\n if (missingNow.length > 0) pendingWarnings.set(form, missingNow);\n\n // Dispatch toolactivated event per spec\n window.dispatchEvent(new CustomEvent('toolactivated', { detail: { toolName } }));\n\n return new Promise<ExecuteResult>((resolve, reject) => {\n const timeoutMs = config.execution.timeoutMs;\n const timeoutId = setTimeout(() => {\n const pending = pendingExecutions.get(form);\n if (!pending) return;\n pendingExecutions.delete(form);\n const timedOutState = (\n config.autoSubmit ||\n form.hasAttribute('toolautosubmit') ||\n form.dataset['webmcpAutosubmit'] !== undefined\n ) ? 'timed_out' : 'awaiting_user_action';\n const warn: FillWarning = {\n field: '__form__',\n type: 'timeout',\n message: timedOutState === 'timed_out'\n ? `tool execution timed out after ${timeoutMs}ms`\n : `waiting for user submit (timed out after ${timeoutMs}ms)`,\n };\n const _existingValsTimeout = preFillValues.get(form);\n const structured: StructuredExecuteData = {\n status: timedOutState,\n filled_fields: serializeFormData(form, lastParams.get(form), formFieldElements.get(form)),\n skipped_fields: [],\n missing_required: pendingWarnings.get(form) ?? [],\n warnings: [...(pendingFillWarnings.get(form) ?? []), warn],\n ...(_existingValsTimeout !== undefined && { existing_values: _existingValsTimeout }),\n };\n pendingWarnings.delete(form);\n pendingFillWarnings.delete(form);\n lastFilledSnapshot.delete(form);\n preFillValues.delete(form);\n resolve({\n content: [\n { type: 'text', text: warn.message },\n { type: 'text', text: JSON.stringify(structured) },\n ],\n });\n }, timeoutMs);\n pendingExecutions.set(form, { resolve, reject, timeoutId });\n\n if (\n config.autoSubmit ||\n form.hasAttribute('toolautosubmit') ||\n form.dataset['webmcpAutosubmit'] !== undefined\n ) {\n // Wait for the form DOM to stabilize before submitting. Frameworks like\n // React 18 batch state updates asynchronously after InputEvents \u2014 a fixed\n // delay is unreliable. waitForDomStable polls for 150 ms of silence (no\n // mutations) and caps at 800 ms so we never hang indefinitely.\n waitForDomStable(form).then(async () => {\n try {\n // Re-fill after framework has committed state updates.\n fillFormFields(form, resolvedParams);\n\n // Retry up to 2 times if the framework reset any filled values.\n for (let attempt = 0; attempt < 2; attempt++) {\n const reset = getResetFields(form, resolvedParams, formFieldElements.get(form));\n if (reset.length === 0) break;\n fillFormFields(form, resolvedParams);\n await waitForDomStable(form, 400, 100);\n }\n\n // If the stored form was remounted (React, Turbo etc.), find the live\n // form via the submit button so requestSubmit() reaches the real DOM.\n let submitForm: HTMLFormElement = form;\n if (!form.isConnected) {\n const liveBtn = document.querySelector<HTMLElement>(\n 'button[type=\"submit\"]:not([disabled]), input[type=\"submit\"]:not([disabled])',\n );\n const found = liveBtn?.closest('form') as HTMLFormElement | null;\n if (found) {\n submitForm = found;\n const pending = pendingExecutions.get(form);\n const nextPending = pending?.timeoutId\n ? { resolve, reject, timeoutId: pending.timeoutId }\n : { resolve, reject };\n pendingExecutions.set(submitForm, nextPending);\n attachSubmitInterceptor(submitForm, toolName);\n }\n }\n\n // If the form was remounted, transfer the pending warnings to the live form.\n if (submitForm !== form && pendingWarnings.has(form)) {\n pendingWarnings.set(submitForm, pendingWarnings.get(form)!);\n pendingWarnings.delete(form);\n }\n\n if (!submitForm.checkValidity()) {\n const pending = pendingExecutions.get(submitForm) ?? pendingExecutions.get(form);\n if (pending) {\n if (pending.timeoutId) clearTimeout(pending.timeoutId);\n pendingExecutions.delete(submitForm);\n pendingExecutions.delete(form);\n const warnings = [\n ...(pendingFillWarnings.get(submitForm) ?? pendingFillWarnings.get(form) ?? []),\n ...collectInvalidFieldWarnings(submitForm),\n ];\n pendingFillWarnings.delete(submitForm);\n pendingFillWarnings.delete(form);\n const _existingValsBlocked = preFillValues.get(form);\n const structured: StructuredExecuteData = {\n status: 'blocked_invalid',\n filled_fields: serializeFormData(submitForm, lastParams.get(submitForm) ?? lastParams.get(form), formFieldElements.get(submitForm) ?? formFieldElements.get(form)),\n skipped_fields: [],\n missing_required: pendingWarnings.get(submitForm) ?? pendingWarnings.get(form) ?? [],\n warnings,\n validation_errors: collectValidationErrors(submitForm),\n ...(_existingValsBlocked !== undefined && { existing_values: _existingValsBlocked }),\n };\n pendingWarnings.delete(submitForm);\n pendingWarnings.delete(form);\n lastFilledSnapshot.delete(submitForm);\n lastFilledSnapshot.delete(form);\n preFillValues.delete(form);\n resolve({\n content: [\n { type: 'text', text: 'Form submission blocked by native validation.' },\n { type: 'text', text: JSON.stringify(structured) },\n ],\n });\n }\n return;\n }\n\n submitForm.requestSubmit();\n } catch (err) {\n reject(err instanceof Error ? err : new Error(String(err)));\n }\n });\n }\n // Otherwise: the form stays filled; human clicks submit,\n // which fires the submit event interceptor below.\n });\n };\n}\n\nfunction attachSubmitInterceptor(form: HTMLFormElement, toolName: string): void {\n // Guard against attaching multiple times\n if ((form as unknown as Record<string, unknown>)['__awmcp_intercepted']) return;\n (form as unknown as Record<string, unknown>)['__awmcp_intercepted'] = true;\n\n form.addEventListener('submit', (e: SubmitEvent) => {\n const pending = pendingExecutions.get(form);\n if (!pending) return; // Normal human submission \u2014 do nothing\n\n // Agent-invoked path\n const { resolve } = pending;\n if (pending.timeoutId) clearTimeout(pending.timeoutId);\n pendingExecutions.delete(form);\n\n const formData = serializeFormData(form, lastParams.get(form), formFieldElements.get(form));\n const existingVals = preFillValues.get(form);\n lastFilledSnapshot.delete(form);\n preFillValues.delete(form);\n const missingRequired = pendingWarnings.get(form) ?? [];\n pendingWarnings.delete(form);\n const fillWarnings = pendingFillWarnings.get(form) ?? [];\n pendingFillWarnings.delete(form);\n\n const skippedFields = fillWarnings\n .filter((w) => w.type === 'not_filled')\n .map((w) => w.field);\n\n const structured: StructuredExecuteData = {\n status: missingRequired.length > 0 || skippedFields.length > 0 ? 'partial' : 'success',\n filled_fields: formData,\n skipped_fields: skippedFields,\n missing_required: missingRequired,\n warnings: [\n ...missingRequired.map((f): FillWarning => ({\n field: f,\n type: 'missing_required',\n message: `required field \"${f}\" was not provided`,\n })),\n ...fillWarnings,\n ],\n ...(existingVals !== undefined && { existing_values: existingVals }),\n };\n\n const allWarnMessages = [\n ...(missingRequired.length ? [`required fields were not filled: ${missingRequired.join(', ')}`] : []),\n ...fillWarnings.map((w) => w.message),\n ];\n const warningText = allWarnMessages.length ? ` Note: ${allWarnMessages.join('; ')}.` : '';\n const text = `Form submitted. Fields: ${JSON.stringify(formData)}${warningText}`;\n const result: ExecuteResult = {\n content: [\n { type: 'text', text },\n { type: 'text', text: JSON.stringify(structured) },\n ],\n };\n\n if (e.agentInvoked && typeof e.respondWith === 'function') {\n // Native WebMCP path: use respondWith to return to browser\n e.preventDefault();\n e.respondWith(Promise.resolve(result));\n }\n resolve(result);\n });\n\n // Dispatch toolcancel when form is reset\n form.addEventListener('reset', () => {\n lastFilledSnapshot.delete(form);\n preFillValues.delete(form);\n window.dispatchEvent(new CustomEvent('toolcancel', { detail: { toolName } }));\n });\n}\n\n// ---------------------------------------------------------------------------\n// Field filling\n// ---------------------------------------------------------------------------\n\nfunction setReactValue(el: HTMLInputElement | HTMLTextAreaElement, v: string): void {\n el.focus();\n // Select all existing text so the insert replaces it\n el.select?.();\n\n // execCommand('insertText') simulates real typing \u2014 triggers native browser\n // input events that every framework (React, Catalyst, Stimulus, Vue, etc.)\n // listens to. Most reliable cross-framework approach.\n // Guard: execCommand can return true but silently fail for inputs inside\n // shadow DOM (e.g. GitHub's Catalyst components), so verify the value landed.\n if (document.execCommand('insertText', false, v) && el.value === v) {\n return;\n }\n\n // Fallback: native prototype setter (bypasses React controlled-input cache)\n const setter = el instanceof HTMLTextAreaElement ? _textareaValueSetter : _inputValueSetter;\n if (setter) {\n setter.call(el, v);\n } else {\n el.value = v;\n }\n el.dispatchEvent(new InputEvent('input', { bubbles: true, cancelable: true, inputType: 'insertText', data: v }));\n el.dispatchEvent(new Event('change', { bubbles: true }));\n}\n\nfunction setReactChecked(el: HTMLInputElement, checked: boolean): void {\n if (_checkedSetter) {\n _checkedSetter.call(el, checked);\n } else {\n el.checked = checked;\n }\n el.dispatchEvent(new Event('change', { bubbles: true }));\n}\n\n/**\n * Recursively search shadow roots for a native form control matching selector.\n * GitHub's Catalyst and other custom-element frameworks render inputs inside\n * shadow DOM that form.querySelector() cannot pierce.\n */\nfunction findInShadowRoots(\n root: Document | ShadowRoot,\n selector: string,\n): HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | null {\n for (const host of Array.from(root.querySelectorAll('*'))) {\n const sr = (host as Element).shadowRoot;\n if (!sr) continue;\n const found = sr.querySelector<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>(selector);\n if (found) return found;\n const deeper = findInShadowRoots(sr, selector);\n if (deeper) return deeper;\n }\n return null;\n}\n\nfunction getAssociatedInputsByName(\n form: HTMLFormElement,\n type: 'checkbox' | 'radio',\n name: string,\n): HTMLInputElement[] {\n return Array.from(form.elements).filter(\n (el): el is HTMLInputElement =>\n el instanceof HTMLInputElement &&\n el.type === type &&\n el.name === name,\n );\n}\n\n/** Find a native form control by name or id, including inside shadow DOM. */\nfunction findNativeField(\n form: HTMLFormElement,\n key: string,\n): HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | null {\n // Includes out-of-tree controls linked with form=\"...\".\n const named = form.elements.namedItem(key);\n if (\n typeof named === 'object' &&\n named !== null &&\n (named instanceof HTMLInputElement || named instanceof HTMLTextAreaElement || named instanceof HTMLSelectElement)\n ) {\n return named;\n }\n if (named instanceof RadioNodeList) {\n const first = named[0];\n const firstObj = first as unknown;\n if (\n firstObj instanceof HTMLInputElement ||\n firstObj instanceof HTMLTextAreaElement ||\n firstObj instanceof HTMLSelectElement\n ) {\n return firstObj;\n }\n }\n\n const esc = CSS.escape(key);\n // Light DOM first (fast path)\n const light =\n form.querySelector<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>(`[name=\"${esc}\"]`) ??\n form.querySelector<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>(\n `input#${esc}, textarea#${esc}, select#${esc}`,\n );\n if (light) return light;\n // Shadow DOM fallback: search all shadow roots in the document\n return (\n findInShadowRoots(document, `[name=\"${esc}\"]`) ??\n findInShadowRoots(document, `input#${esc}, textarea#${esc}, select#${esc}`)\n );\n}\n\nfunction fillFormFields(form: HTMLFormElement, params: Record<string, unknown>): void {\n lastParams.set(form, params);\n const fieldEls = formFieldElements.get(form);\n const snapshot: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(params)) {\n const input = findNativeField(form, key);\n\n if (input) {\n if (input instanceof HTMLInputElement) {\n fillInput(input, form, key, value);\n if (input.type === 'checkbox') {\n if (Array.isArray(value)) {\n snapshot[key] = getAssociatedInputsByName(form, 'checkbox', key)\n .filter((b) => b.checked)\n .map((b) => b.value);\n } else {\n snapshot[key] = input.checked;\n }\n } else {\n snapshot[key] = input.value;\n }\n } else if (input instanceof HTMLTextAreaElement) {\n setReactValue(input, String(value ?? ''));\n snapshot[key] = input.value;\n } else if (input instanceof HTMLSelectElement) {\n fillSelectElement(input, value, form, key);\n snapshot[key] = input.multiple\n ? Array.from(input.options).filter((o) => o.selected).map((o) => o.value)\n : input.value;\n }\n continue;\n }\n\n // Fall back to id-keyed or ARIA-role element from the field map.\n const ariaEl = fieldEls?.get(key);\n if (ariaEl) {\n // If the stored element was remounted by a framework (React, Catalyst etc.),\n // find a fresh reference using its actual DOM id (which may differ from the\n // sanitized schema key \u2014 e.g. \"repository-name-input\" \u2192 key \"repository_name_input\").\n let effectiveEl: Element = ariaEl;\n if (!ariaEl.isConnected) {\n const elId = (ariaEl as HTMLElement).id;\n if (elId) {\n const fresh =\n document.getElementById(elId) ??\n findInShadowRoots(document, `#${CSS.escape(elId)}`);\n if (fresh) effectiveEl = fresh;\n }\n }\n if (effectiveEl instanceof HTMLInputElement) {\n fillInput(effectiveEl, form, key, value);\n snapshot[key] = effectiveEl.type === 'checkbox' ? effectiveEl.checked : effectiveEl.value;\n } else if (effectiveEl instanceof HTMLTextAreaElement) {\n setReactValue(effectiveEl, String(value ?? ''));\n snapshot[key] = effectiveEl.value;\n } else if (effectiveEl instanceof HTMLSelectElement) {\n fillSelectElement(effectiveEl, value, form, key);\n snapshot[key] = effectiveEl.multiple\n ? Array.from(effectiveEl.options).filter((o) => o.selected).map((o) => o.value)\n : effectiveEl.value;\n } else {\n fillAriaField(effectiveEl, value);\n snapshot[key] = value; // Use the raw value for ARIA elements (no reliable DOM readback)\n }\n }\n }\n\n lastFilledSnapshot.set(form, snapshot);\n\n // Expose fill warnings for external readers (extension, bridge).\n // The WeakMap is not accessible outside the IIFE closure, so we mirror the\n // current warnings onto window so the extension can read them after filling.\n (window as unknown as Record<string, unknown>)['__lastFillWarnings'] =\n pendingFillWarnings.get(form) ?? [];\n}\n\nfunction fillInput(\n input: HTMLInputElement,\n form: HTMLFormElement,\n key: string,\n value: unknown,\n): void {\n const type = input.type.toLowerCase();\n\n if (type === 'checkbox') {\n // Agent may pass an array for checkbox groups (multiple checkboxes with same name)\n if (Array.isArray(value)) {\n const allBoxes = getAssociatedInputsByName(form, 'checkbox', key);\n for (const box of allBoxes) {\n setReactChecked(box, (value as unknown[]).map(String).includes(box.value));\n }\n return;\n }\n setReactChecked(input, Boolean(value));\n return;\n }\n\n if (type === 'number' || type === 'range') {\n const raw = String(value ?? '');\n const num = Number(raw);\n if (raw === '' || isNaN(num)) {\n pendingFillWarnings.get(form)?.push({\n field: key,\n type: 'type_mismatch',\n message: `\"${key}\" expects a number, got: ${JSON.stringify(value)}`,\n original: value,\n });\n return;\n }\n const min = input.min !== '' ? parseFloat(input.min) : -Infinity;\n const max = input.max !== '' ? parseFloat(input.max) : Infinity;\n if (num < min || num > max) {\n const clamped = Math.min(Math.max(num, min), max);\n pendingFillWarnings.get(form)?.push({\n field: key,\n type: 'clamped',\n message: `\"${key}\" value ${num} is outside allowed range [${input.min || '?'}, ${input.max || '?'}], clamped to ${clamped}`,\n original: num,\n actual: clamped,\n });\n input.value = String(clamped);\n } else {\n input.value = String(num);\n }\n input.dispatchEvent(new InputEvent('input', { bubbles: true, cancelable: true, inputType: 'insertText', data: String(num) }));\n input.dispatchEvent(new Event('change', { bubbles: true }));\n return;\n }\n\n if (type === 'radio') {\n const radios = getAssociatedInputsByName(form, 'radio', key);\n for (const radio of radios) {\n if (radio.value === String(value)) {\n if (_checkedSetter) {\n _checkedSetter.call(radio, true);\n } else {\n radio.checked = true;\n }\n radio.dispatchEvent(new Event('change', { bubbles: true }));\n break;\n }\n }\n return;\n }\n\n setReactValue(input, String(value ?? ''));\n}\n\n/**\n * Fill a select element. For multi-select, deselects all options then selects\n * those whose value is in the agent-supplied array. A single string value is\n * treated as a one-element array for multi-select. For single-select, tries\n * exact value match first, then case-insensitive label match as fallback.\n */\nfunction fillSelectElement(\n select: HTMLSelectElement,\n value: unknown,\n form?: HTMLFormElement,\n key?: string,\n): void {\n if (select.multiple) {\n const vals: string[] = Array.isArray(value)\n ? (value as unknown[]).map(String)\n : [String(value ?? '')];\n for (const opt of Array.from(select.options)) {\n opt.selected = vals.includes(opt.value);\n }\n select.dispatchEvent(new Event('change', { bubbles: true }));\n return;\n }\n\n const strVal = String(value ?? '');\n select.value = strVal;\n\n // If the value didn't match any option (browser silently ignores unknown values),\n // try case-insensitive label/text matching as a fallback.\n if (select.value !== strVal) {\n const lower = strVal.toLowerCase();\n const byLabel = Array.from(select.options).find(\n (o) => o.text.trim().toLowerCase() === lower || o.label.trim().toLowerCase() === lower,\n );\n if (byLabel) {\n select.value = byLabel.value;\n } else if (form && key) {\n // Neither value nor label matched \u2014 record a not_filled warning.\n pendingFillWarnings.get(form)?.push({\n field: key,\n type: 'not_filled',\n message: `\"${key}\" value \"${strVal}\" did not match any option in the select`,\n original: strVal,\n });\n }\n }\n\n select.dispatchEvent(new Event('change', { bubbles: true }));\n}\n\nfunction fillAriaField(el: Element, value: unknown): void {\n const role = el.getAttribute('role');\n\n if (role === 'checkbox' || role === 'switch') {\n el.setAttribute('aria-checked', String(Boolean(value)));\n el.dispatchEvent(new MouseEvent('click', { bubbles: true }));\n return;\n }\n\n if (role === 'radio') {\n el.setAttribute('aria-checked', 'true');\n el.dispatchEvent(new MouseEvent('click', { bubbles: true }));\n return;\n }\n\n if (role === 'radiogroup') {\n // Find the matching radio option inside the group and activate it\n const radios = Array.from(el.querySelectorAll('[role=\"radio\"]'));\n for (const radio of radios) {\n const val = (radio.getAttribute('data-value') ?? radio.getAttribute('aria-label') ?? radio.textContent ?? '').trim();\n if (val === String(value)) {\n radio.setAttribute('aria-checked', 'true');\n radio.dispatchEvent(new MouseEvent('click', { bubbles: true }));\n for (const other of radios) {\n if (other !== radio) other.setAttribute('aria-checked', 'false');\n }\n break;\n }\n }\n return;\n }\n\n // textbox, combobox, searchbox, spinbutton\n const htmlEl = el as HTMLElement;\n console.log('[auto-webmcp] fillAriaField', {\n tag: el.tagName, role, isContentEditable: htmlEl.isContentEditable,\n id: el.id, ariaLabel: el.getAttribute('aria-label'),\n textContentBefore: (htmlEl.textContent ?? '').slice(0, 80),\n });\n\n if (htmlEl.isContentEditable) {\n htmlEl.focus();\n\n // Select all content scoped to this element (not document.execCommand('selectAll')\n // which can confuse Quill/ProseMirror by triggering their select-all handling).\n const range = document.createRange();\n range.selectNodeContents(htmlEl);\n const sel = window.getSelection();\n sel?.removeAllRanges();\n sel?.addRange(range);\n\n const text = String(value ?? '');\n console.log('[auto-webmcp] fillAriaField: text to insert:', JSON.stringify(text));\n\n // Strategy 1: paste simulation (Draft.js preferred path).\n // Draft.js intercepts paste, reads clipboardData, updates EditorState, and\n // enables submit buttons. We check whether text actually landed in the DOM\n // after the event \u2014 if the editor handled paste but clipboardData was empty\n // (a Chrome security edge-case for synthetic events) we fall through.\n // NOTE: use .trim() \u2014 an empty Quill/ProseMirror editor may contain '<p><br></p>'\n // whose textContent is '\\n' (length 1), which would falsely indicate success.\n let inserted = false;\n try {\n const dt = new DataTransfer();\n dt.setData('text/plain', text);\n htmlEl.dispatchEvent(new ClipboardEvent('paste', {\n bubbles: true, cancelable: true, composed: true, clipboardData: dt,\n }));\n inserted = (htmlEl.textContent ?? '').trim().length > 0;\n console.log('[auto-webmcp] fillAriaField: S1 paste result:', inserted, JSON.stringify((htmlEl.textContent ?? '').slice(0, 80)));\n } catch (e) {\n console.log('[auto-webmcp] fillAriaField: S1 paste threw:', e);\n }\n\n if (!inserted) {\n // Strategy 2: execCommand insertText with the range selection active.\n // The browser replaces the selected content with the new text and fires\n // the native beforeinput + input events that Quill/ProseMirror listen to.\n const ok = document.execCommand('insertText', false, text);\n inserted = (htmlEl.textContent ?? '').trim().length > 0;\n console.log('[auto-webmcp] fillAriaField: S2 execCommand result:', ok, 'inserted:', inserted, JSON.stringify((htmlEl.textContent ?? '').slice(0, 80)));\n }\n\n if (!inserted) {\n // Strategy 3: direct beforeinput InputEvent (ProseMirror / LinkedIn editor).\n // Dispatching directly guarantees event.data === text; execCommand may fire\n // beforeinput with data=null in some Chrome versions.\n try {\n htmlEl.dispatchEvent(new InputEvent('beforeinput', {\n bubbles: true, cancelable: true, composed: true,\n inputType: 'insertText', data: text,\n }));\n inserted = (htmlEl.textContent ?? '').trim().length > 0;\n console.log('[auto-webmcp] fillAriaField: S3 beforeinput result:', inserted, JSON.stringify((htmlEl.textContent ?? '').slice(0, 80)));\n } catch (e) {\n console.log('[auto-webmcp] fillAriaField: S3 beforeinput threw:', e);\n }\n }\n\n if (!inserted) {\n // Strategy 4: direct textContent assignment (MutationObserver-based editors).\n // Sets content directly in the DOM; editors using MutationObserver (Quill,\n // ProseMirror) will pick it up and sync their internal model.\n htmlEl.textContent = text;\n const r2 = document.createRange();\n r2.selectNodeContents(htmlEl);\n r2.collapse(false);\n sel?.removeAllRanges();\n sel?.addRange(r2);\n console.log('[auto-webmcp] fillAriaField: S4 textContent assignment done, textContent:', JSON.stringify((htmlEl.textContent ?? '').slice(0, 80)));\n }\n\n // Always dispatch input so any remaining framework listeners are notified.\n htmlEl.dispatchEvent(new InputEvent('input', {\n bubbles: true, cancelable: true, inputType: 'insertText', data: text,\n }));\n console.log('[auto-webmcp] fillAriaField: done, final textContent:', JSON.stringify((htmlEl.textContent ?? '').slice(0, 80)));\n } else {\n console.log('[auto-webmcp] fillAriaField: not contentEditable, dispatching input/change only');\n el.dispatchEvent(new Event('input', { bubbles: true }));\n el.dispatchEvent(new Event('change', { bubbles: true }));\n }\n}\n\n// ---------------------------------------------------------------------------\n// Serialization\n// ---------------------------------------------------------------------------\n\nfunction serializeFormData(\n form: HTMLFormElement,\n params?: Record<string, unknown>,\n fieldEls?: Map<string, Element>,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n const data = new FormData(form);\n const snapshot = lastFilledSnapshot.get(form);\n\n for (const [key, val] of data.entries()) {\n if (result[key] !== undefined) {\n // Multiple values \u2192 array\n const existing = result[key];\n if (Array.isArray(existing)) {\n existing.push(val);\n } else {\n result[key] = [existing, val];\n }\n } else {\n result[key] = val;\n }\n }\n\n // Supplement with id-keyed or ARIA-keyed fields not captured by FormData.\n // Prefer the post-fill snapshot over a live DOM query: the DOM may have been\n // reset by a framework re-render (React reconciliation) between fill and submit.\n if (params) {\n for (const key of Object.keys(params)) {\n if (key in result) continue;\n\n if (snapshot && key in snapshot) {\n result[key] = snapshot[key];\n continue;\n }\n\n const el =\n findNativeField(form, key) ??\n fieldEls?.get(key) ??\n null;\n if (!el) continue;\n if (el instanceof HTMLInputElement && el.type === 'checkbox') {\n result[key] = el.checked;\n } else if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement) {\n result[key] = el.value;\n } else {\n // ARIA element\n const role = el.getAttribute('role');\n if (role === 'checkbox' || role === 'switch') {\n result[key] = el.getAttribute('aria-checked') === 'true';\n } else {\n result[key] = (el as HTMLElement).textContent?.trim() ?? '';\n }\n }\n }\n }\n\n return result;\n}\n\n/**\n * Fill a single form control or ARIA element with the given value.\n * Exported for use by orphan-input (formless) tool handlers in discovery.ts.\n */\n/**\n * Convert ISO date strings (YYYY-MM-DD) to MM/DD/YYYY for text inputs that\n * do not use type=\"date\". Browsers handle ISO natively for type=\"date\"; for\n * type=\"text\" date fields (Salesforce Lightning, many CRMs), the expected\n * format is typically MM/DD/YYYY. Only converts when the field name or id\n * contains \"date\" (case-insensitive) to avoid false positives.\n */\nfunction maybeConvertIsoDate(value: string, el: HTMLElement): string {\n const isoMatch = value.match(/^(\\d{4})-(\\d{2})-(\\d{2})$/);\n if (!isoMatch) return value;\n if (el instanceof HTMLInputElement && el.type === 'date') return value;\n const fieldHint = ((el as HTMLInputElement).name ?? el.id ?? '').toLowerCase();\n if (!/date/.test(fieldHint)) return value;\n const [, year, month, day] = isoMatch;\n return `${month}/${day}/${year}`;\n}\n\nexport function fillElement(\n el: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | Element,\n value: unknown,\n): void {\n if (el instanceof HTMLInputElement) {\n const type = el.type.toLowerCase();\n if (type === 'checkbox') {\n setReactChecked(el, Boolean(value));\n } else if (type === 'radio') {\n if (el.value === String(value)) {\n if (_checkedSetter) _checkedSetter.call(el, true);\n else el.checked = true;\n el.dispatchEvent(new Event('change', { bubbles: true }));\n }\n } else {\n setReactValue(el, maybeConvertIsoDate(String(value ?? ''), el));\n }\n } else if (el instanceof HTMLTextAreaElement) {\n setReactValue(el, String(value ?? ''));\n } else if (el instanceof HTMLSelectElement) {\n fillSelectElement(el, value);\n } else {\n fillAriaField(el, value);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers for DOM stabilization, reset detection, and required-field warnings\n// ---------------------------------------------------------------------------\n\n/**\n * Resolves when the form DOM has been stable (no mutations) for debounceMs,\n * or when maxMs has elapsed. Replaces the hardcoded 300 ms pre-submit delay\n * so React/Vue/etc. can finish committing batched state updates.\n */\nfunction waitForDomStable(\n form: HTMLFormElement,\n maxMs = 800,\n debounceMs = 150,\n): Promise<void> {\n return new Promise<void>((resolve) => {\n let settled = false;\n let debounceTimer: ReturnType<typeof setTimeout> | null = null;\n\n const settle = (): void => {\n if (settled) return;\n settled = true;\n observer.disconnect();\n if (debounceTimer !== null) clearTimeout(debounceTimer);\n resolve();\n };\n\n const observer = new MutationObserver(() => {\n if (debounceTimer !== null) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(settle, debounceMs);\n });\n\n observer.observe(form, { childList: true, subtree: true, attributes: true, characterData: true });\n\n setTimeout(settle, maxMs); // hard cap\n debounceTimer = setTimeout(settle, debounceMs); // resolves early if already stable\n });\n}\n\n/**\n * Returns the keys of params whose corresponding DOM field values no longer\n * match what was filled, indicating the framework reset them.\n */\nfunction getResetFields(\n form: HTMLFormElement,\n params: Record<string, unknown>,\n fieldEls: Map<string, Element> | undefined,\n): string[] {\n const reset: string[] = [];\n for (const [key, expected] of Object.entries(params)) {\n const el = findNativeField(form, key) ?? (fieldEls?.get(key) ?? null);\n if (!el) continue;\n if (el instanceof HTMLInputElement && el.type === 'checkbox') {\n if (el.checked !== Boolean(expected)) reset.push(key);\n } else if (\n el instanceof HTMLInputElement ||\n el instanceof HTMLTextAreaElement ||\n el instanceof HTMLSelectElement\n ) {\n if (el.value !== String(expected ?? '')) reset.push(key);\n }\n }\n return reset;\n}\n\n/**\n * Returns required field keys from the schema that the agent did not supply.\n * Uses the `in` operator so fields explicitly passed as empty string or false\n * are not flagged (intentional empty values are valid).\n */\nfunction getMissingRequired(\n metadata: ToolMetadata | undefined,\n params: Record<string, unknown>,\n): string[] {\n if (!metadata?.inputSchema?.required?.length) return [];\n return metadata.inputSchema.required.filter((fieldKey) => !(fieldKey in params));\n}\n\n/**\n * Async fill for `<button role=\"combobox\">` elements (Salesforce Lightning,\n * Atlaskit, and other JS-powered dropdowns). The pattern:\n * 1. Click the button to open the dropdown.\n * 2. Wait for a [role=\"listbox\"] to appear (up to 1s).\n * 3. Click the option whose data-value, aria-label, or text matches `value`.\n *\n * Exported for use by the orphan execute handler in discovery.ts.\n */\nexport async function fillComboboxButton(el: Element, value: unknown): Promise<void> {\n const text = String(value ?? '').trim();\n console.log('[auto-webmcp] fillComboboxButton: clicking button, value=', JSON.stringify(text));\n el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));\n\n // Wait for a listbox to appear (dropdown opens asynchronously in most frameworks).\n const listbox = await new Promise<Element | null>((resolve) => {\n const deadline = Date.now() + 3000;\n const poll = (): void => {\n // Search from the nearest overlay root or document body.\n // Salesforce Lightning renders its dropdown portal in the main document body\n // (not in shadow DOM), so document.querySelector() works once it is mounted.\n const candidate =\n document.querySelector('[role=\"listbox\"]') ??\n document.querySelector('[role=\"option\"]')?.closest('[role=\"listbox\"]') ??\n null;\n if (candidate) {\n resolve(candidate);\n return;\n }\n if (Date.now() >= deadline) { resolve(null); return; }\n setTimeout(poll, 50);\n };\n poll();\n });\n\n if (!listbox) {\n console.warn('[auto-webmcp] fillComboboxButton: listbox did not appear after 1s');\n return;\n }\n\n const options = Array.from(listbox.querySelectorAll('[role=\"option\"]'));\n console.log('[auto-webmcp] fillComboboxButton: listbox has', options.length, 'options');\n\n const lowerValue = text.toLowerCase();\n const match = options.find((opt) => {\n const dataValue = (opt.getAttribute('data-value') ?? '').toLowerCase();\n const ariaLabel = (opt.getAttribute('aria-label') ?? '').toLowerCase();\n const optText = (opt.textContent ?? '').trim().toLowerCase();\n return dataValue === lowerValue || ariaLabel === lowerValue || optText === lowerValue;\n });\n\n if (match) {\n console.log('[auto-webmcp] fillComboboxButton: clicking option', match.textContent?.trim());\n match.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));\n } else {\n console.warn('[auto-webmcp] fillComboboxButton: no option matched', JSON.stringify(text),\n 'available:', options.map((o) => o.textContent?.trim()));\n }\n}\n", "/**\n * discovery.ts \u2014 Form scanning & MutationObserver for SPA support\n */\n\nimport { ResolvedConfig } from './config.js';\nimport { analyzeForm, analyzeOrphanInputGroup, ToolAnnotations } from './analyzer.js';\nimport {\n registerFormTool,\n unregisterFormTool,\n isWebMCPSupported,\n getAllRegisteredTools,\n getRegisteredToolName,\n} from './registry.js';\nimport { buildExecuteHandler, fillElement, fillComboboxButton } from './interceptor.js';\nimport { ARIA_ROLES_TO_SCAN, JsonSchema } from './schema.js';\n\n// ---------------------------------------------------------------------------\n// Events\n// ---------------------------------------------------------------------------\n\nexport type FormLifecycleEvent = CustomEvent<{\n form: HTMLFormElement;\n toolName: string;\n}>;\n\nfunction emit(type: 'form:registered' | 'form:unregistered', form: HTMLFormElement, toolName: string): void {\n window.dispatchEvent(\n new CustomEvent(type, { detail: { form, toolName } }) as FormLifecycleEvent,\n );\n}\n\n// ---------------------------------------------------------------------------\n// Registration helpers\n// ---------------------------------------------------------------------------\n\n/** Check whether a form should be excluded per config */\nfunction isExcluded(form: HTMLFormElement, config: ResolvedConfig): boolean {\n // Skip forms with data-no-webmcp\n if (form.dataset['noWebmcp'] !== undefined) return true;\n // Skip per config exclude list\n for (const selector of config.exclude) {\n try {\n if (form.matches(selector)) return true;\n } catch {\n // invalid selector \u2014 ignore\n }\n }\n return false;\n}\n\nfunction withNumericSuffix(baseName: string, n: number): string {\n const suffix = `_${n}`;\n return `${baseName.slice(0, Math.max(1, 64 - suffix.length))}${suffix}`;\n}\n\nfunction getUsedToolNames(excludeForm?: HTMLFormElement): Set<string> {\n const names = new Set<string>(registeredOrphanToolNames);\n for (const { form, name } of getAllRegisteredTools()) {\n if (excludeForm && form === excludeForm) continue;\n names.add(name);\n }\n return names;\n}\n\nfunction ensureUniqueToolName(baseName: string, excludeForm?: HTMLFormElement): string {\n const used = getUsedToolNames(excludeForm);\n if (!used.has(baseName)) return baseName;\n let i = 2;\n let candidate = withNumericSuffix(baseName, i);\n while (used.has(candidate)) {\n i++;\n candidate = withNumericSuffix(baseName, i);\n }\n return candidate;\n}\n\nfunction hasNativeDeclarativeTool(form: HTMLFormElement): boolean {\n return form.getAttribute('toolname')?.trim().length ? true : false;\n}\n\nasync function registerForm(form: HTMLFormElement, config: ResolvedConfig): Promise<void> {\n if (isExcluded(form, config)) return;\n const previousName = getRegisteredToolName(form);\n\n if (hasNativeDeclarativeTool(form) && config.declarativeMode !== 'force') {\n if (previousName) {\n await unregisterFormTool(form);\n }\n if (config.debug) {\n const mode = config.declarativeMode;\n console.log(`[auto-webmcp] Skipping imperative registration for native declarative form (mode=${mode})`);\n }\n return;\n }\n\n // Find matching override (first matching selector wins)\n let override;\n for (const [selector, ovr] of Object.entries(config.overrides)) {\n try {\n if (form.matches(selector)) {\n override = ovr;\n break;\n }\n } catch {\n // invalid selector\n }\n }\n\n const metadata = analyzeForm(form, override);\n const resolvedName = ensureUniqueToolName(metadata.name, form);\n if (resolvedName !== metadata.name && config.debug) {\n console.warn(`[auto-webmcp] tool name collision: \"${metadata.name}\" renamed to \"${resolvedName}\"`);\n }\n metadata.name = resolvedName;\n\n if (config.debug) {\n warnToolQuality(metadata.name, metadata.description);\n }\n\n const execute = buildExecuteHandler(form, config, metadata.name, metadata);\n\n await registerFormTool(form, metadata, execute);\n registeredForms.add(form);\n registeredFormCount++;\n\n // Expose the submit button reference so background.js can click it via\n // direct DOM reference instead of relying on CSS selector matching.\n const formSubmitBtn = form.querySelector<HTMLButtonElement | HTMLInputElement>(\n '[type=\"submit\"], button[data-variant=\"primary\"], button:not([type])',\n ) ?? null;\n const pendingBtns = ((window as unknown as Record<string, unknown>)['__pendingSubmitBtns'] ??= {}) as Record<string, Element | null>;\n if (previousName && previousName !== metadata.name) {\n delete pendingBtns[previousName];\n }\n pendingBtns[metadata.name] = formSubmitBtn;\n\n if (config.debug) {\n console.log(`[auto-webmcp] Registered: ${metadata.name}`, metadata);\n }\n\n emit('form:registered', form, metadata.name);\n}\n\nasync function unregisterForm(form: HTMLFormElement, config: ResolvedConfig): Promise<void> {\n const name = getRegisteredToolName(form);\n if (!name) return;\n\n await unregisterFormTool(form);\n registeredForms.delete(form);\n const pendingBtns = (window as unknown as Record<string, unknown>)['__pendingSubmitBtns'] as Record<string, Element | null> | undefined;\n if (pendingBtns) delete pendingBtns[name];\n\n if (config.debug) {\n console.log(`[auto-webmcp] Unregistered: ${name}`);\n }\n\n emit('form:unregistered', form, name);\n}\n\n// ---------------------------------------------------------------------------\n// MutationObserver\n// ---------------------------------------------------------------------------\n\nlet observer: MutationObserver | null = null;\n\n/** Set of currently registered forms, used to detect lazy-rendered child inputs. */\nconst registeredForms = new WeakSet<HTMLFormElement>();\n\n/** Count of forms registered in the current discovery session. Reset on each startDiscovery call. */\nlet registeredFormCount = 0;\n\n/** Debounce timers for re-analysis when inputs are added to existing forms. */\nconst reAnalysisTimers = new Map<HTMLFormElement, ReturnType<typeof setTimeout>>();\nconst RE_ANALYSIS_DEBOUNCE_MS = 300;\n\n/**\n * Debounce timer for re-running scanOrphanInputs when the DOM gains new\n * orphan-eligible elements (e.g. Salesforce Lightning modals, SPA dialogs).\n * Stored at module scope so the timer persists across observer callbacks.\n */\nlet orphanRescanTimer: ReturnType<typeof setTimeout> | null = null;\nconst ORPHAN_RESCAN_DEBOUNCE_MS = 500;\n\n/**\n * Secondary delayed rescan timer for complex SPAs (e.g. Salesforce Lightning)\n * where custom elements render their shadow DOM asynchronously, meaning the\n * 500ms debounce fires before all fields are ready.\n */\nlet orphanRescanDelayedTimer: ReturnType<typeof setTimeout> | null = null;\nconst ORPHAN_RESCAN_DELAYED_MS = 2000;\n\n/** Names of already-registered orphan tools. Prevents double-registration when\n * the observer fires multiple times for the same modal opening. */\nconst registeredOrphanToolNames = new Set<string>();\n\nfunction scheduleOrphanRescan(config: ResolvedConfig): void {\n if (orphanRescanTimer) clearTimeout(orphanRescanTimer);\n orphanRescanTimer = setTimeout(() => {\n orphanRescanTimer = null;\n void scanOrphanInputs(config);\n }, ORPHAN_RESCAN_DEBOUNCE_MS);\n}\n\n/**\n * Schedule a second, longer-delayed orphan rescan for custom elements that\n * render their shadow DOM asynchronously (e.g. Salesforce Lightning Web Components\n * populate their shadow roots after connectedCallback, which can take 1-2s).\n */\nfunction scheduleOrphanRescanDelayed(config: ResolvedConfig): void {\n if (orphanRescanDelayedTimer) clearTimeout(orphanRescanDelayedTimer);\n orphanRescanDelayedTimer = setTimeout(() => {\n orphanRescanDelayedTimer = null;\n void scanOrphanInputs(config);\n }, ORPHAN_RESCAN_DELAYED_MS);\n}\n\n/** Returns true if node is (or contains) an input-like or ARIA-role element. */\nfunction isInterestingNode(node: Element): boolean {\n const tag = node.tagName.toLowerCase();\n if (tag === 'input' || tag === 'textarea' || tag === 'select') return true;\n // Custom elements (e.g. Salesforce LWC: lightning-input, lightning-record-edit-form)\n // render their inputs inside shadow DOM which querySelectorAll() cannot pierce.\n // Treat any custom element as potentially interesting so we schedule a shadow rescan.\n if (tag.includes('-')) return true;\n const role = node.getAttribute('role');\n if (role && (ARIA_ROLES_TO_SCAN as readonly string[]).includes(role)) return true;\n if (node.querySelector('input, textarea, select')) return true;\n for (const r of ARIA_ROLES_TO_SCAN) {\n if (node.querySelector(`[role=\"${r}\"]`)) return true;\n }\n return false;\n}\n\nfunction scheduleReAnalysis(form: HTMLFormElement, config: ResolvedConfig): void {\n const existing = reAnalysisTimers.get(form);\n if (existing) clearTimeout(existing);\n reAnalysisTimers.set(\n form,\n setTimeout(() => {\n reAnalysisTimers.delete(form);\n void registerForm(form, config);\n }, RE_ANALYSIS_DEBOUNCE_MS),\n );\n}\n\nfunction scheduleFormReAnalysisById(formId: string, config: ResolvedConfig): void {\n const owner = document.getElementById(formId);\n if (owner instanceof HTMLFormElement && registeredForms.has(owner)) {\n scheduleReAnalysis(owner, config);\n }\n}\n\nfunction resolveOwnerForm(el: Element): HTMLFormElement | null {\n const closest = el.closest('form');\n if (closest instanceof HTMLFormElement) return closest;\n const explicitOwner = (el as HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | HTMLButtonElement).form;\n if (explicitOwner instanceof HTMLFormElement) return explicitOwner;\n const formId = el.getAttribute('form');\n if (formId) {\n const byId = document.getElementById(formId);\n if (byId instanceof HTMLFormElement) return byId;\n }\n return null;\n}\n\nfunction startObserver(config: ResolvedConfig): void {\n if (observer) return;\n\n observer = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n if (mutation.type === 'attributes' && mutation.target instanceof Element) {\n const target = mutation.target;\n const ownerForm = resolveOwnerForm(target);\n if (ownerForm && registeredForms.has(ownerForm)) {\n scheduleReAnalysis(ownerForm, config);\n } else if (target instanceof HTMLFormElement) {\n void registerForm(target, config);\n } else if (isInterestingNode(target) && !target.closest('form')) {\n scheduleOrphanRescan(config);\n }\n\n if (mutation.attributeName === 'form' && mutation.oldValue) {\n scheduleFormReAnalysisById(mutation.oldValue, config);\n }\n continue;\n }\n\n for (const node of mutation.addedNodes) {\n if (!(node instanceof Element)) continue;\n\n if (node instanceof HTMLFormElement) {\n void registerForm(node, config);\n continue;\n }\n\n // Newly added child inside an already-registered form?\n const parentForm = node.closest('form');\n if (parentForm instanceof HTMLFormElement && registeredForms.has(parentForm) && isInterestingNode(node)) {\n scheduleReAnalysis(parentForm, config);\n }\n\n // New forms nested inside the added subtree\n for (const form of Array.from(node.querySelectorAll<HTMLFormElement>('form'))) {\n void registerForm(form, config);\n }\n\n // If the added node contains inputs that are NOT inside any <form>,\n // schedule a debounced orphan re-scan. This covers SPAs like Salesforce\n // Lightning where a modal with inputs is injected after initial page load.\n if (isInterestingNode(node) && !node.closest('form')) {\n scheduleOrphanRescan(config);\n // Custom elements (tag names with hyphens, e.g. lightning-record-edit-form)\n // populate their shadow DOM asynchronously after connectedCallback.\n // Fire a second scan at 2s to catch fields that weren't ready at 500ms.\n if (node.tagName.toLowerCase().includes('-')) {\n scheduleOrphanRescanDelayed(config);\n }\n }\n }\n\n for (const node of mutation.removedNodes) {\n if (!(node instanceof Element)) continue;\n\n const forms = node instanceof HTMLFormElement\n ? [node]\n : Array.from(node.querySelectorAll<HTMLFormElement>('form'));\n\n for (const form of forms) {\n void unregisterForm(form, config);\n }\n }\n }\n });\n\n observer.observe(document.body, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeOldValue: true,\n });\n}\n\n// ---------------------------------------------------------------------------\n// SPA route change support\n// ---------------------------------------------------------------------------\n\nfunction listenForRouteChanges(config: ResolvedConfig): void {\n // Hash changes\n window.addEventListener('hashchange', () => scanForms(config));\n\n // History API (pushState / replaceState)\n const original = {\n pushState: history.pushState.bind(history),\n replaceState: history.replaceState.bind(history),\n };\n\n history.pushState = function (...args) {\n original.pushState(...args);\n scanForms(config);\n };\n\n history.replaceState = function (...args) {\n original.replaceState(...args);\n scanForms(config);\n };\n\n window.addEventListener('popstate', () => scanForms(config));\n}\n\n// ---------------------------------------------------------------------------\n// Main scan\n// ---------------------------------------------------------------------------\n\nasync function scanForms(config: ResolvedConfig): Promise<void> {\n const forms = Array.from(document.querySelectorAll<HTMLFormElement>('form'));\n await Promise.allSettled(forms.map((form) => registerForm(form, config)));\n}\n\n// ---------------------------------------------------------------------------\n// Orphan input scanner (fallback for pages with no <form> elements)\n// ---------------------------------------------------------------------------\n\n/** Input types that are never useful to expose to agents */\nconst ORPHAN_EXCLUDED_TYPES = new Set([\n 'password', 'hidden', 'file', 'submit', 'reset', 'button', 'image',\n]);\n\n/**\n * Recursively collect input-like elements from shadow roots throughout the document.\n * Returns an array of `{ el, shadowHost }` pairs where `shadowHost` is the outermost\n * shadow-host element that lives in the regular (non-shadow) DOM. This is used as\n * the anchor for grouping inputs by their nearest submit button.\n *\n * This covers Salesforce Lightning Web Components where fields like `lightning-input`\n * render a native `<input>` inside nested shadow roots invisible to querySelectorAll().\n */\nfunction collectShadowOrphanInputs(\n root: Element | ShadowRoot,\n outerHost: Element | null,\n visited = new Set<Element | ShadowRoot>(),\n): Array<{ el: HTMLElement; shadowHost: Element }> {\n if (visited.has(root)) return [];\n visited.add(root);\n\n const results: Array<{ el: HTMLElement; shadowHost: Element }> = [];\n\n for (const el of Array.from(root.querySelectorAll('*'))) {\n const sr = el.shadowRoot;\n if (!sr) continue;\n\n // The outermost host in the regular DOM is the first element that has a\n // shadow root encountered while walking from the document root.\n const host = outerHost ?? el;\n results.push(...collectShadowOrphanInputs(sr, host, visited));\n }\n\n // Collect inputs directly inside this shadow root level.\n if (root instanceof ShadowRoot) {\n const selector =\n 'input, textarea, select, ' +\n '[role=\"textbox\"]:not(input):not(textarea), ' +\n '[role=\"searchbox\"]:not(input):not(textarea), ' +\n 'button[role=\"combobox\"]';\n for (const el of Array.from(root.querySelectorAll<HTMLElement>(selector))) {\n if (el instanceof HTMLInputElement && ORPHAN_EXCLUDED_TYPES.has(el.type.toLowerCase())) continue;\n // Skip if the element is inside a <form> within this shadow root.\n if (el.closest('form')) continue;\n const rect = el.getBoundingClientRect();\n if (rect.width === 0 || rect.height === 0) continue;\n if (outerHost) {\n results.push({ el, shadowHost: outerHost });\n }\n }\n }\n\n return results;\n}\n\n/**\n * Find all visible form controls that are NOT inside a <form> element,\n * group them by their nearest ancestor that also contains a submit button,\n * and register each group as a WebMCP tool.\n *\n * This covers common patterns on landing pages and Ghost blogs where the\n * subscribe/search UI is built from plain inputs + buttons without a <form> tag.\n */\nasync function scanOrphanInputs(config: ResolvedConfig): Promise<void> {\n if (!isWebMCPSupported()) return;\n\n // Matches enabled submit buttons: traditional [type=\"submit\"] plus primary-variant buttons\n // used by React design systems (GitHub/Primer: data-variant=\"primary\") that use type=\"button\"\n // with JavaScript click handlers instead of native form submission.\n const SUBMIT_BTN_SELECTOR = '[type=\"submit\"]:not([disabled]), button[data-variant=\"primary\"]:not([disabled])';\n // Includes disabled variants \u2014 used only to identify which container is the \"form group\".\n // Many sites (GitHub) start with the submit button disabled until fields are filled.\n const SUBMIT_BTN_GROUPING_SELECTOR = '[type=\"submit\"], button[data-variant=\"primary\"]';\n const SUBMIT_TEXT_RE = /subscribe|submit|sign[\\s-]?up|send|join|go|search|post|tweet|publish|save/i;\n\n // Collect visible inputs that are not inside a <form>.\n // Includes native controls AND ARIA textbox/contenteditable elements (e.g. Twitter/X\n // compose box uses role=\"textbox\" on a contenteditable div, not a native <textarea>).\n const orphanInputs = (Array.from(\n document.querySelectorAll(\n 'input:not(form input), textarea:not(form textarea), select:not(form select), ' +\n '[role=\"textbox\"]:not(form [role=\"textbox\"]):not(input):not(textarea), ' +\n '[role=\"searchbox\"]:not(form [role=\"searchbox\"]):not(input):not(textarea), ' +\n '[contenteditable=\"true\"]:not(form [contenteditable=\"true\"]):not(input):not(textarea), ' +\n // button[role=\"combobox\"] covers JS-powered dropdowns (Salesforce Lightning Stage/Type/Lead Source,\n // Atlaskit Select) where a <button> opens a listbox instead of a native <select>.\n 'button[role=\"combobox\"]:not(form button[role=\"combobox\"])',\n ),\n ) as Array<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | HTMLElement>).filter((el) => {\n if (el instanceof HTMLInputElement && ORPHAN_EXCLUDED_TYPES.has(el.type.toLowerCase())) {\n console.log(`[auto-webmcp] orphan: skipping excluded type \"${el.type}\" (name=\"${el.name}\" id=\"${el.id}\")`);\n return false;\n }\n const rect = el.getBoundingClientRect();\n if (rect.width === 0 || rect.height === 0) {\n console.log(`[auto-webmcp] orphan: skipping invisible input (name=\"${(el as HTMLElement & { name?: string }).name}\" id=\"${el.id}\")`);\n return false;\n }\n return true;\n });\n\n // Also collect inputs buried inside shadow DOM (e.g. Salesforce LWC lightning-input components).\n const shadowOrphans = collectShadowOrphanInputs(document.body, null);\n console.log(`[auto-webmcp] orphan: found ${orphanInputs.length} light-DOM + ${shadowOrphans.length} shadow-DOM orphan inputs`);\n\n if (orphanInputs.length === 0 && shadowOrphans.length === 0) return;\n\n // Group inputs by the nearest ancestor that also contains a submit button.\n // Walk up from each input until we find a container with a submit-like button.\n const groups = new Map<Element, Array<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | HTMLElement>>();\n\n for (const input of orphanInputs) {\n let container: Element | null = input.parentElement;\n let foundContainer: Element = input.parentElement ?? document.body;\n\n while (container && container !== document.body) {\n const hasSubmitBtn =\n container.querySelector(SUBMIT_BTN_GROUPING_SELECTOR) !== null ||\n Array.from(container.querySelectorAll('button')).some(\n (b) => SUBMIT_TEXT_RE.test(b.textContent ?? ''),\n );\n if (hasSubmitBtn) {\n foundContainer = container;\n break;\n }\n container = container.parentElement;\n }\n\n console.log(`[auto-webmcp] orphan: input (name=\"${(input as HTMLElement & { name?: string }).name}\" id=\"${input.id}\") grouped into container`, foundContainer);\n if (!groups.has(foundContainer)) groups.set(foundContainer, []);\n groups.get(foundContainer)!.push(input);\n }\n\n // Group shadow DOM inputs by walking up from their outermost shadow host.\n for (const { el, shadowHost } of shadowOrphans) {\n let container: Element | null = shadowHost.parentElement;\n let foundContainer: Element = shadowHost.parentElement ?? document.body;\n\n while (container && container !== document.body) {\n const hasSubmitBtn =\n container.querySelector(SUBMIT_BTN_GROUPING_SELECTOR) !== null ||\n Array.from(container.querySelectorAll('button')).some(\n (b) => SUBMIT_TEXT_RE.test(b.textContent ?? ''),\n );\n if (hasSubmitBtn) {\n foundContainer = container;\n break;\n }\n container = container.parentElement;\n }\n\n console.log(`[auto-webmcp] orphan (shadow): input (id=\"${el.id}\") via host <${shadowHost.tagName.toLowerCase()}> grouped into container`, foundContainer);\n if (!groups.has(foundContainer)) groups.set(foundContainer, []);\n groups.get(foundContainer)!.push(el);\n }\n\n console.log(`[auto-webmcp] orphan: ${groups.size} group(s) found`);\n\n for (const [container, inputs] of groups) {\n // Pick the last visible submit button within the container (same logic as\n // handleCallTool in background.js: primary action is always the last one).\n const allCandidates = Array.from(\n container.querySelectorAll<HTMLButtonElement | HTMLInputElement>(SUBMIT_BTN_SELECTOR),\n ).filter((b) => {\n const r = b.getBoundingClientRect();\n return r.width > 0 && r.height > 0;\n });\n\n let submitBtn: HTMLButtonElement | HTMLInputElement | null =\n (allCandidates[allCandidates.length - 1] as HTMLButtonElement | HTMLInputElement) ?? null;\n\n // Fallback 1: disabled [type=\"submit\"] buttons in the container.\n // Many sites (GitHub new-issue form) start with the submit button disabled until\n // required fields are filled. Use the disabled button as the reference for tool\n // naming; the execute handler will poll for it to become enabled before clicking.\n if (!submitBtn) {\n const disabledCandidates = Array.from(\n container.querySelectorAll<HTMLButtonElement | HTMLInputElement>(SUBMIT_BTN_GROUPING_SELECTOR),\n ).filter((b) => {\n const r = b.getBoundingClientRect();\n return r.width > 0 && r.height > 0 && (b as HTMLButtonElement).disabled;\n });\n submitBtn = (disabledCandidates[disabledCandidates.length - 1] as HTMLButtonElement | HTMLInputElement) ?? null;\n if (submitBtn) console.log(`[auto-webmcp] orphan: using disabled submit button as reference: \"${submitBtn.textContent?.trim()}\"`);\n }\n\n // Fallback 2: any visible button with submit-like text WITHIN the container.\n // Catches React-style buttons (Twitter \"Post\", LinkedIn \"Share\") that use\n // type=\"button\" with no variant class, scoped to the container so we don't\n // pick up unrelated buttons elsewhere on the page (e.g. sidebar).\n // Also includes [role=\"button\"] to cover div/span-based buttons (Gmail Send).\n if (!submitBtn) {\n const containerBtns = Array.from(\n container.querySelectorAll<HTMLElement>('button, [role=\"button\"]'),\n ).filter((b) => {\n const r = b.getBoundingClientRect();\n return r.width > 0 && r.height > 0 &&\n !(b as HTMLButtonElement).disabled &&\n b.getAttribute('aria-disabled') !== 'true' &&\n SUBMIT_TEXT_RE.test(b.textContent ?? '');\n });\n submitBtn = (containerBtns[containerBtns.length - 1] as HTMLButtonElement | null) ?? null;\n if (submitBtn) console.log(`[auto-webmcp] orphan: using text-matched button in container: \"${submitBtn.textContent?.trim()}\"`);\n }\n\n // Fallback 3: nearest dialog/modal ancestor \u2014 catches buttons like LinkedIn's\n // \"Post\" that sit outside the input container but inside the same dialog.\n // Two-pass: first try ANY disabled button (HTML disabled OR aria-disabled) that\n // matches the submit keyword. Disabled buttons in compose dialogs are almost\n // always the real submit button waiting for content, not settings/config buttons\n // (which are always enabled). If no disabled ones, fall back to enabled buttons.\n if (!submitBtn) {\n const dialog = container.closest('[role=\"dialog\"], [aria-modal=\"true\"]');\n if (dialog) {\n const allDialogBtns = Array.from(\n dialog.querySelectorAll<HTMLElement>('button, [role=\"button\"]'),\n ).filter((b) => {\n const r = b.getBoundingClientRect();\n return r.width > 0 && r.height > 0 && SUBMIT_TEXT_RE.test(b.textContent ?? '');\n });\n console.log(`[auto-webmcp] orphan: dialog buttons matching submit text:`,\n allDialogBtns.map(b => `\"${b.textContent?.trim().slice(0, 30)}\" disabled=${(b as HTMLButtonElement).disabled} aria-disabled=${b.getAttribute('aria-disabled')}`));\n // Pass 1: any form of disabled (HTML or ARIA) \u2014 real submit button waiting for input\n const disabledBtns = allDialogBtns.filter(\n (b) => (b as HTMLButtonElement).disabled || b.getAttribute('aria-disabled') === 'true',\n );\n // Pass 2: enabled buttons \u2014 settings/config buttons are always enabled\n const enabledBtns = allDialogBtns.filter(\n (b) => !(b as HTMLButtonElement).disabled && b.getAttribute('aria-disabled') !== 'true',\n );\n const dialogBtns = disabledBtns.length > 0 ? disabledBtns : enabledBtns;\n submitBtn = (dialogBtns[dialogBtns.length - 1] as HTMLButtonElement | null) ?? null;\n if (submitBtn) console.log(`[auto-webmcp] orphan: using text-matched button in dialog: \"${submitBtn.textContent?.trim().slice(0, 40)}\" disabled=${(submitBtn as HTMLButtonElement).disabled} aria-disabled=${submitBtn.getAttribute('aria-disabled')}`);\n }\n }\n\n // Fallback 4: nearest button with submit-like text anywhere on the page.\n // Includes [role=\"button\"] for div/span-based buttons (Gmail, Outlook, etc.).\n if (!submitBtn) {\n const pageBtns = Array.from(\n document.querySelectorAll<HTMLElement>('button, [role=\"button\"]'),\n ).filter((b) => {\n const r = b.getBoundingClientRect();\n return r.width > 0 && r.height > 0 &&\n b.getAttribute('aria-disabled') !== 'true' &&\n SUBMIT_TEXT_RE.test(b.textContent ?? '');\n });\n submitBtn = (pageBtns[pageBtns.length - 1] as HTMLButtonElement | null) ?? null;\n if (submitBtn) console.log(`[auto-webmcp] orphan: using page-wide fallback submit button: \"${submitBtn.textContent?.trim()}\"`);\n }\n\n console.log(`[auto-webmcp] orphan: submit button for group:`, submitBtn ? `\"${submitBtn.textContent?.trim()}\" disabled=${(submitBtn as HTMLButtonElement).disabled}` : 'none');\n\n const metadata = analyzeOrphanInputGroup(container, inputs, submitBtn);\n // Same orphan group can be discovered repeatedly; skip duplicates by base name.\n if (registeredOrphanToolNames.has(metadata.name)) {\n console.log(`[auto-webmcp] orphan: \"${metadata.name}\" already registered, skipping`);\n continue;\n }\n const orphanName = ensureUniqueToolName(metadata.name);\n if (orphanName !== metadata.name && config.debug) {\n console.warn(`[auto-webmcp] orphan tool name collision: \"${metadata.name}\" renamed to \"${orphanName}\"`);\n }\n metadata.name = orphanName;\n console.log(`[auto-webmcp] orphan: tool=\"${metadata.name}\" schema keys:`, Object.keys(metadata.inputSchema.properties));\n\n // Build key \u2192 element pairs for the execute handler\n const inputPairs: Array<{ key: string; el: HTMLElement }> = [];\n const schemaProps = metadata.inputSchema.properties;\n // Auto-generated IDs (React: _r_1_, _r_c_) are not meaningful schema keys.\n // Skip them so aria-label or placeholder can provide a better key.\n const AUTO_ID_RE = /^_r_[0-9a-z]+_$/i;\n for (const el of inputs) {\n const id = el.id && !AUTO_ID_RE.test(el.id) ? el.id : null;\n // Use getAttribute for name/placeholder so this works for both native controls\n // and ARIA/contenteditable elements (which lack .name and .placeholder properties).\n const key =\n (el as HTMLInputElement).name ||\n el.getAttribute('name') ||\n (el as HTMLElement).dataset['webmcpName'] ||\n id ||\n el.getAttribute('aria-label') ||\n el.getAttribute('placeholder') ||\n null;\n const safeKey = key\n ? key.toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, '').slice(0, 64)\n : null;\n const matched = !!(safeKey && schemaProps[safeKey]);\n console.log(`[auto-webmcp] orphan: field (name=\"${(el as HTMLInputElement).name ?? ''}\" id=\"${el.id}\") rawKey=\"${key}\" safeKey=\"${safeKey}\" matched=${matched}`);\n if (matched) {\n inputPairs.push({ key: safeKey!, el });\n }\n }\n\n console.log(`[auto-webmcp] orphan: ${inputPairs.length}/${inputs.length} input(s) mapped to schema keys`);\n\n if (inputPairs.length === 0) {\n console.log(`[auto-webmcp] orphan: skipping group \"${metadata.name}\" \u2014 no inputs mapped to schema keys`);\n continue;\n }\n\n const toolName = metadata.name;\n const execute = async (\n params: Record<string, unknown>,\n _client?: unknown,\n ): Promise<{ content: Array<{ type: 'text'; text: string }> }> => {\n console.log(`[auto-webmcp] orphan execute: tool=\"${toolName}\" params=`, params);\n console.log(`[auto-webmcp] orphan execute: inputPairs=`, inputPairs.map(p => p.key));\n\n for (const { key, el } of inputPairs) {\n if (params[key] !== undefined) {\n console.log(`[auto-webmcp] orphan execute: filling key=\"${key}\" value=`, params[key], 'element=', el);\n // button[role=\"combobox\"] (Salesforce Lightning, Atlaskit) requires async fill:\n // click to open listbox, wait for options to render, click the matching option.\n if (el.getAttribute('role') === 'combobox' && el.tagName.toLowerCase() === 'button') {\n await fillComboboxButton(el, params[key]);\n } else {\n fillElement(el, params[key]);\n }\n console.log(`[auto-webmcp] orphan execute: after fill, element value=`, (el as HTMLInputElement).value);\n } else {\n console.log(`[auto-webmcp] orphan execute: key=\"${key}\" not in params, skipping`);\n }\n }\n window.dispatchEvent(new CustomEvent('toolactivated', { detail: { toolName } }));\n\n const shouldAutoSubmit =\n config.autoSubmit ||\n !!submitBtn?.hasAttribute('toolautosubmit') ||\n (submitBtn instanceof HTMLElement && submitBtn.dataset['webmcpAutosubmit'] !== undefined) ||\n container.hasAttribute('toolautosubmit') ||\n (container instanceof HTMLElement && container.dataset['webmcpAutosubmit'] !== undefined);\n\n if (!shouldAutoSubmit) {\n console.log(`[auto-webmcp] orphan execute: autoSubmit=false, returning without clicking submit`);\n return { content: [{ type: 'text', text: 'Fields filled. Ready to submit.' }] };\n }\n\n // Find the enabled submit button to click.\n // Priority: captured submitBtn reference (still in DOM + enabled) \u2192 selector poll (React/Vue\n // enable-on-valid pattern) \u2192 text-matched button (Salesforce type=\"button\" Save, Gmail Send).\n console.log(`[auto-webmcp] orphan execute: resolving submit button (up to 2s)...`);\n let btn: HTMLButtonElement | HTMLInputElement | HTMLElement | null = null;\n\n // Check the captured reference first (avoids 2s timeout for always-enabled buttons like Salesforce Save).\n if (submitBtn && document.contains(submitBtn)) {\n const isEnabled = !(submitBtn as HTMLButtonElement).disabled &&\n submitBtn.getAttribute('aria-disabled') !== 'true';\n const r = submitBtn.getBoundingClientRect();\n if (isEnabled && r.width > 0 && r.height > 0) {\n btn = submitBtn;\n console.log(`[auto-webmcp] orphan execute: using captured submit button \"${btn.textContent?.trim()}\"`);\n }\n }\n\n if (!btn) {\n // Poll for a [type=\"submit\"] or primary-variant button to become enabled\n // (handles React/Vue re-renders that enable the button after field validation).\n const deadline = Date.now() + 2000;\n while (Date.now() < deadline) {\n const candidates = Array.from(\n container.querySelectorAll<HTMLButtonElement | HTMLInputElement>(SUBMIT_BTN_SELECTOR),\n ).filter((b) => {\n const r = b.getBoundingClientRect();\n return r.width > 0 && r.height > 0;\n });\n const last = candidates[candidates.length - 1] ?? null;\n if (last) { btn = last; break; }\n await new Promise<void>((r) => setTimeout(r, 100));\n }\n }\n\n // Final fallback: any enabled text-matched button in the container or page.\n if (!btn) {\n const textBtns = Array.from(\n (container !== document.body ? container : document).querySelectorAll<HTMLElement>('button, [role=\"button\"]'),\n ).filter((b) => {\n const r = b.getBoundingClientRect();\n return r.width > 0 && r.height > 0 &&\n !(b as HTMLButtonElement).disabled &&\n b.getAttribute('aria-disabled') !== 'true' &&\n SUBMIT_TEXT_RE.test(b.textContent ?? '');\n });\n btn = textBtns[textBtns.length - 1] ?? null;\n if (btn) console.log(`[auto-webmcp] orphan execute: using text-matched fallback button \"${btn.textContent?.trim()}\"`);\n }\n\n if (!btn) {\n console.warn(`[auto-webmcp] orphan execute: submit button still disabled after 2s`);\n return { content: [{ type: 'text', text: 'Fields filled but the submit button is still disabled. The page may require additional input before submitting.' }] };\n }\n\n console.log(`[auto-webmcp] orphan execute: clicking submit button \"${(btn as HTMLElement).textContent?.trim()}\"`);\n (btn as HTMLElement).click();\n return { content: [{ type: 'text', text: 'Fields filled and form submitted.' }] };\n };\n\n try {\n const toolDef: {\n name: string;\n description: string;\n inputSchema: JsonSchema;\n annotations?: ToolAnnotations;\n execute: (\n params: Record<string, unknown>,\n client?: unknown,\n ) => Promise<{ content: Array<{ type: 'text'; text: string }> }>;\n } = {\n name: metadata.name,\n description: metadata.description,\n inputSchema: metadata.inputSchema,\n execute,\n };\n if (metadata.annotations && Object.keys(metadata.annotations).length > 0) {\n toolDef.annotations = metadata.annotations;\n }\n await navigator.modelContext!.registerTool(toolDef);\n registeredOrphanToolNames.add(metadata.name);\n // Expose the submit button reference so background.js can click it via CDP.\n // GitHub and other React apps use type=\"button\" (not type=\"submit\"), so\n // background.js cannot find the button by selector alone.\n const pendingBtns = ((window as unknown as Record<string, unknown>)['__pendingSubmitBtns'] ??= {}) as Record<string, Element | null>;\n pendingBtns[metadata.name] = submitBtn;\n if (config.debug) {\n console.log(`[auto-webmcp] Orphan tool registered: ${metadata.name}`, metadata);\n }\n } catch {\n // Best-effort\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nfunction warnToolQuality(name: string, description: string): void {\n if (/^form_\\d+$|^submit$|^form$/.test(name)) {\n console.warn(`[auto-webmcp] Tool \"${name}\" has a generic name. Consider adding a toolname or data-webmcp-name attribute.`);\n }\n if (!description || description === 'Submit form') {\n console.warn(`[auto-webmcp] Tool \"${name}\" has no meaningful description.`);\n }\n if (/don'?t|do not|never|avoid|not for/i.test(description)) {\n console.warn(`[auto-webmcp] Tool \"${name}\" description contains negative instructions. Per spec best practices, prefer positive descriptions.`);\n }\n}\n\nexport async function startDiscovery(config: ResolvedConfig): Promise<void> {\n if (document.readyState === 'loading') {\n await new Promise<void>((resolve) =>\n document.addEventListener('DOMContentLoaded', () => resolve(), { once: true }),\n );\n }\n\n registeredFormCount = 0;\n registeredOrphanToolNames.clear();\n startObserver(config);\n listenForRouteChanges(config);\n await scanForms(config);\n\n // Always scan for orphan inputs (inputs outside <form> elements) in addition to form-based tools.\n // Many modern SPAs (GitHub issues, Twitter compose, LinkedIn) render inputs without a <form> wrapper.\n await scanOrphanInputs(config);\n}\n\nexport function stopDiscovery(): void {\n observer?.disconnect();\n observer = null;\n}\n"],
|
|
5
|
-
"mappings": "wcAAA,IAAAA,GAAA,GAAAC,GAAAD,GAAA,gBAAAE,KC2FO,SAASC,GAAcC,EAA+C,CAC3E,IAAMC,EAASD,GAAY,cAAc,QAAU,GAC7CE,EACJD,EAAS,GAASD,GAAY,cAAc,uBAAyB,GACvE,MAAO,CACL,QAASA,GAAY,SAAW,CAAC,EACjC,WAAYA,GAAY,YAAc,GACtC,gBAAiBA,GAAY,iBAAmB,OAChD,aAAc,CACZ,OAAAC,EACA,sBAAAC,CACF,EACA,UAAW,CACT,UAAW,KAAK,IAAI,IAAKF,GAAY,WAAW,WAAa,IAAK,CACpE,EACA,UAAWA,GAAY,WAAa,CAAC,EACrC,iBAAkBA,GAAY,kBAAoB,GAClD,MAAOA,GAAY,OAAS,EAC9B,CACF,CC1GO,IAAMG,EAAqB,CAChC,UAAW,WAAY,WAAY,QAAS,SAC5C,aAAc,YAAa,QAC7B,EA4BO,SAASC,GACdC,EAC2B,CAC3B,OAAIA,aAAiB,iBACZC,GAAgBD,CAAK,EAE1BA,aAAiB,oBACZ,CAAE,KAAM,QAAS,EAEtBA,aAAiB,kBACZE,GAAiBF,CAAK,EAExB,IACT,CAEA,SAASC,GAAgBD,EAAoD,CAG3E,OAFaA,EAAM,KAAK,YAAY,EAEtB,CACZ,IAAK,OACL,IAAK,SACL,IAAK,MACH,OAAOG,EAAkBH,CAAK,EAEhC,IAAK,QACH,MAAO,CAAE,GAAGG,EAAkBH,CAAK,EAAG,OAAQ,OAAQ,EAExD,IAAK,MACH,MAAO,CAAE,GAAGG,EAAkBH,CAAK,EAAG,OAAQ,KAAM,EAEtD,IAAK,SACL,IAAK,QAAS,CACZ,IAAMI,EAA2B,CAAE,KAAM,QAAS,EAClD,OAAIJ,EAAM,MAAQ,KAAII,EAAK,QAAU,WAAWJ,EAAM,GAAG,GACrDA,EAAM,MAAQ,KAAII,EAAK,QAAU,WAAWJ,EAAM,GAAG,GAClDI,CACT,CAEA,IAAK,OACH,MAAO,CAAE,KAAM,SAAU,OAAQ,MAAO,EAE1C,IAAK,iBACH,MAAO,CAAE,KAAM,SAAU,OAAQ,WAAY,EAE/C,IAAK,OACH,MAAO,CAAE,KAAM,SAAU,OAAQ,MAAO,EAE1C,IAAK,QACH,MAAO,CAAE,KAAM,SAAU,QAAS,iBAAkB,EAEtD,IAAK,OACH,MAAO,CAAE,KAAM,SAAU,QAAS,kBAAmB,EAEvD,IAAK,QACH,MAAO,CAAE,KAAM,SAAU,QAAS,mBAAoB,EAExD,IAAK,WACH,MAAO,CAAE,KAAM,SAAU,EAE3B,IAAK,QAEH,MAAO,CAAE,KAAM,QAAS,EAE1B,IAAK,OACL,IAAK,SACL,IAAK,SACL,IAAK,QACL,IAAK,SACL,IAAK,QAEH,OAAO,KAET,IAAK,WAEH,OAAO,KAET,QACE,MAAO,CAAE,KAAM,QAAS,CAC5B,CACF,CAEA,SAASD,EAAkBH,EAA6C,CACtE,IAAMI,EAA2B,CAAE,KAAM,QAAS,EAC9CJ,EAAM,UAAY,IAAGI,EAAK,UAAYJ,EAAM,WAC5CA,EAAM,UAAY,GAAKA,EAAM,YAAc,SAAQI,EAAK,UAAYJ,EAAM,WAC1EA,EAAM,UAASI,EAAK,QAAUJ,EAAM,SAIxC,IAAMK,EAASL,EAAM,aAAa,MAAM,EACxC,GAAIK,EAAQ,CACV,IAAMC,EAAWN,EAAM,cAAc,eAAeK,CAAM,EAC1D,GAAIC,aAAoB,oBAAqB,CAC3C,IAAMC,EAAU,MAAM,KAAKD,EAAS,OAAO,EAAE,OAC1CE,GAAM,CAACA,EAAE,UAAYA,EAAE,MAAM,KAAK,IAAM,EAC3C,EACID,EAAQ,OAAS,IACnBH,EAAK,KAAOG,EAAQ,IAAKC,GAAMA,EAAE,MAAM,KAAK,CAAC,EAC7CJ,EAAK,MAAQG,EAAQ,IAAKC,IAAO,CAC/B,MAAOA,EAAE,MAAM,KAAK,EACpB,MAAOA,EAAE,aAAa,KAAK,GAAKA,EAAE,MAAM,KAAK,CAC/C,EAAE,EAEN,CACF,CAEA,OAAOJ,CACT,CAOA,IAAMK,GAAuB,oCAW7B,SAASC,GAAoBC,EAAiC,CAC5D,OAAIA,EAAI,SAAiB,GACrBA,EAAI,QAAU,GAAW,GACtBF,GAAqB,KAAKE,EAAI,KAAK,KAAK,CAAC,CAClD,CAEA,SAAST,GAAiBU,EAA+C,CACvE,IAAMC,EAAuB,CAAC,EACxBC,EAAiE,CAAC,EAExE,QAAWC,KAAS,MAAM,KAAKH,EAAO,QAAQ,EAC5C,GAAIG,aAAiB,oBAAqB,CACxC,GAAIA,EAAM,SAAU,SACpB,IAAMC,EAAaD,EAAM,OAAO,KAAK,GAAK,GAC1C,QAAWJ,KAAO,MAAM,KAAKI,EAAM,QAAQ,EAAG,CAE5C,GADI,EAAEJ,aAAe,oBACjBD,GAAoBC,CAAG,EAAG,SAC9BE,EAAW,KAAKF,EAAI,KAAK,EACzB,IAAMM,EAA0D,CAC9D,MAAON,EAAI,MACX,MAAOA,EAAI,KAAK,KAAK,GAAKA,EAAI,KAChC,EACIK,IAAYC,EAAM,MAAQD,GAC9BF,EAAM,KAAKG,CAAK,CAClB,CACF,SAAWF,aAAiB,kBAAmB,CAC7C,GAAIL,GAAoBK,CAAK,EAAG,SAChCF,EAAW,KAAKE,EAAM,KAAK,EAC3BD,EAAM,KAAK,CAAE,MAAOC,EAAM,MAAO,MAAOA,EAAM,KAAK,KAAK,GAAKA,EAAM,KAAM,CAAC,CAC5E,CAGF,OAAIF,EAAW,SAAW,EAAU,CAAE,KAAM,QAAS,EAEjDD,EAAO,SAEF,CAAE,KAAM,QAAS,MAAO,CAAE,KAAM,SAAU,KAAMC,CAAW,CAAE,EAG/D,CAAE,KAAM,SAAU,KAAMA,EAAY,MAAAC,CAAM,CACnD,CAGO,SAASI,GAAoBC,EAAuBC,EAAwB,CACjF,OAAO,MAAM,KAAKD,EAAK,QAAQ,EAC5B,OACEE,GACCA,aAAc,kBACdA,EAAG,OAAS,YACZA,EAAG,OAASD,CAChB,EACC,IAAKE,GAAOA,EAAG,KAAK,EACpB,OAAQC,GAAMA,IAAM,IAAMA,IAAM,IAAI,CACzC,CAGO,SAASC,GAAiBL,EAAuBC,EAAwB,CAO9E,OANe,MAAM,KAAKD,EAAK,QAAQ,EAAE,OACtCE,GACCA,aAAc,kBACdA,EAAG,OAAS,SACZA,EAAG,OAASD,CAChB,EACc,IAAKK,GAAMA,EAAE,KAAK,EAAE,OAAQF,GAAMA,IAAM,EAAE,CAC1D,CAGO,SAASG,GACdP,EACAC,EACyC,CAUzC,OATe,MAAM,KAAKD,EAAK,QAAQ,EACpC,OACEE,GACCA,aAAc,kBACdA,EAAG,OAAS,SACZA,EAAG,OAASD,CAChB,EACC,OAAQK,GAAMA,EAAE,QAAU,EAAE,EAEjB,IAAKA,GAAM,CACvB,IAAME,EAAQC,GAAkBH,CAAC,EACjC,MAAO,CAAE,MAAOA,EAAE,MAAO,MAAOE,GAASF,EAAE,KAAM,CACnD,CAAC,CACH,CAGO,SAASI,GAAiBR,EAAaS,EAAoC,CAChF,OAAQA,EAAM,CACZ,IAAK,WACL,IAAK,SACH,MAAO,CAAE,KAAM,SAAU,EAE3B,IAAK,aACL,IAAK,SAAU,CACb,IAAM1B,EAA2B,CAAE,KAAM,QAAS,EAC5C2B,EAAMV,EAAG,aAAa,eAAe,EACrCW,EAAMX,EAAG,aAAa,eAAe,EAC3C,OAAIU,IAAQ,OAAM3B,EAAK,QAAU,WAAW2B,CAAG,GAC3CC,IAAQ,OAAM5B,EAAK,QAAU,WAAW4B,CAAG,GACxC5B,CACT,CAEA,IAAK,WAAY,CACf,IAAM6B,EAAUZ,EAAG,aAAa,WAAW,GAAKA,EAAG,aAAa,eAAe,EAC/E,GAAIY,EAAS,CACX,IAAMC,EAAU,SAAS,eAAeD,CAAO,EAC/C,GAAIC,EAAS,CACX,IAAM3B,EAAU,MAAM,KAAK2B,EAAQ,iBAAiB,iBAAiB,CAAC,EAAE,OACrE1B,GAAMA,EAAE,aAAa,eAAe,IAAM,MAC7C,EACA,GAAID,EAAQ,OAAS,EAAG,CACtB,IAAMM,EAAaN,EAChB,IAAKC,IAAOA,EAAE,aAAa,YAAY,GAAKA,EAAE,aAAe,IAAI,KAAK,CAAC,EACvE,OAAO,OAAO,EACXM,EAAQP,EAAQ,IAAKC,IAAO,CAChC,OAAQA,EAAE,aAAa,YAAY,GAAKA,EAAE,aAAe,IAAI,KAAK,EAClE,OAAQA,EAAE,aAAe,IAAI,KAAK,CACpC,EAAE,EACF,MAAO,CAAE,KAAM,SAAU,KAAMK,EAAY,MAAAC,CAAM,CACnD,CACF,CACF,CACA,MAAO,CAAE,KAAM,QAAS,CAC1B,CAEA,IAAK,UACL,IAAK,YACL,IAAK,QACL,QACE,MAAO,CAAE,KAAM,QAAS,CAC5B,CACF,CAEA,SAASc,GAAkBO,EAAiC,CAE1D,IAAMC,EAASD,EAAM,QAAQ,OAAO,EACpC,GAAIC,EAAQ,CACV,IAAMC,EAAQD,EAAO,UAAU,EAAI,EACnCC,EAAM,iBAAiB,iCAAiC,EAAE,QAAShB,GAAOA,EAAG,OAAO,CAAC,EACrF,IAAMiB,EAAOD,EAAM,aAAa,KAAK,GAAK,GAC1C,GAAIC,EAAM,OAAOA,CACnB,CAEA,GAAIH,EAAM,GAAI,CACZ,IAAMI,EAAQ,SAAS,cAAgC,cAAc,IAAI,OAAOJ,EAAM,EAAE,CAAC,IAAI,EAC7F,GAAII,EAAO,CACT,IAAMD,EAAOC,EAAM,aAAa,KAAK,GAAK,GAC1C,GAAID,EAAM,OAAOA,CACnB,CACF,CACA,MAAO,EACT,CChSA,IAAIE,GAAY,EAQT,SAASC,GAAYC,EAAuBC,EAAuC,CACxF,IAAMC,EAAOD,GAAU,MAAQE,GAAcH,CAAI,EAC3CI,EAAcH,GAAU,aAAeI,GAAqBL,CAAI,EAChE,CAAE,OAAQM,EAAa,cAAAC,CAAc,EAAIC,GAAYR,CAAI,EACzDS,EAAcC,GAAiBV,CAAI,EAEzC,MAAO,CAAE,KAAAE,EAAM,YAAAE,EAAa,YAAAE,EAAa,YAAAG,EAAa,cAAAF,CAAc,CACtE,CAMA,SAASJ,GAAcH,EAA+B,CAEpD,IAAMW,EAAaX,EAAK,aAAa,UAAU,EAC/C,GAAIW,EAAY,OAAOC,EAAaD,CAAU,EAG9C,IAAME,EAAWb,EAAK,QAAQ,WAC9B,GAAIa,EAAU,OAAOD,EAAaC,CAAQ,EAG1C,IAAMC,EAAaC,GAAoBf,CAAI,EAC3C,GAAIc,EAAY,OAAOF,EAAaE,CAAU,EAG9C,IAAME,EAAUC,GAAsBjB,CAAI,EAC1C,GAAIgB,EAAS,OAAOJ,EAAaI,CAAO,EAGxC,GAAIhB,EAAK,GAAI,OAAOY,EAAaZ,EAAK,EAAE,EACxC,GAAIA,EAAK,KAAM,OAAOY,EAAaZ,EAAK,IAAI,EAG5C,GAAIA,EAAK,OAAQ,CACf,IAAMkB,EAAUC,GAAmBnB,EAAK,MAAM,EAC9C,GAAIkB,EAAS,OAAON,EAAaM,CAAO,CAC1C,CAGA,MAAO,QAAQ,EAAEE,EAAS,EAC5B,CAEA,SAASR,EAAaS,EAAqB,CACzC,OAAOA,EACJ,YAAY,EACZ,KAAK,EACL,QAAQ,cAAe,GAAG,EAC1B,QAAQ,WAAY,EAAE,EACtB,MAAM,EAAG,EAAE,GAAK,MACrB,CAEA,SAASN,GAAoBf,EAA+B,CAC1D,IAAMsB,EAAU,CACd,GAAG,MAAM,KAAKtB,EAAK,iBAAoC,2CAA2C,CAAC,EACnG,GAAG,MAAM,KAAKA,EAAK,iBAAmC,sBAAsB,CAAC,CAC/E,EAEA,QAAWuB,KAAOD,EAAS,CACzB,IAAME,EACJD,aAAe,iBACXA,EAAI,MAAM,KAAK,EACfA,EAAI,aAAa,KAAK,GAAK,GACjC,GAAIC,GAAQA,EAAK,OAAS,GAAKA,EAAK,OAAS,GAAI,OAAOA,CAC1D,CACA,MAAO,EACT,CAEA,SAASP,GAAsBjB,EAA+B,CAE5D,IAAIyB,EAAuBzB,EAC3B,KAAOyB,GAAM,CAEX,IAAIC,EAAUD,EAAK,uBACnB,KAAOC,GAAS,CACd,GAAI,YAAY,KAAKA,EAAQ,OAAO,EAAG,CACrC,IAAMF,EAAOE,EAAQ,aAAa,KAAK,GAAK,GAC5C,GAAIF,EAAM,OAAOA,CACnB,CACAE,EAAUA,EAAQ,sBACpB,CAGA,GAFAD,EAAOA,EAAK,cAER,CAACA,GAAQA,IAAS,SAAS,KAAM,KACvC,CACA,MAAO,EACT,CAEA,SAASN,GAAmBQ,EAAqB,CAC/C,GAAI,CAEF,IAAMC,EADS,IAAI,IAAID,EAAK,OAAO,SAAS,IAAI,EACxB,SAAS,MAAM,GAAG,EAAE,OAAO,OAAO,EAC1D,OAAOC,EAASA,EAAS,OAAS,CAAC,GAAK,EAC1C,MAAQ,CACN,MAAO,EACT,CACF,CAMA,SAASvB,GAAqBL,EAA+B,CAE3D,IAAM6B,EAAa7B,EAAK,aAAa,iBAAiB,EACtD,GAAI6B,EAAY,OAAOA,EAAW,KAAK,EAGvC,IAAMhB,EAAWb,EAAK,QAAQ,kBAC9B,GAAIa,EAAU,OAAOA,EAAS,KAAK,EAGnC,IAAMiB,EAAS9B,EAAK,cAAc,QAAQ,EAC1C,GAAI8B,GAAQ,aAAa,KAAK,EAAG,OAAOA,EAAO,YAAY,KAAK,EAGhE,IAAMC,EAAY/B,EAAK,aAAa,YAAY,EAChD,GAAI+B,GAAW,KAAK,EAAG,OAAOA,EAAU,KAAK,EAG7C,IAAMC,EAAgBhC,EAAK,aAAa,kBAAkB,EAC1D,GAAIgC,EAAe,CACjB,IAAMC,EAAS,SAAS,eAAeD,CAAa,EACpD,GAAIC,GAAQ,aAAa,KAAK,EAAG,OAAOA,EAAO,YAAY,KAAK,CAClE,CAGA,IAAMjB,EAAUC,GAAsBjB,CAAI,EACpCkC,EAAY,SAAS,OAAO,KAAK,EACvC,OAAIlB,GAAWkB,EAAkB,GAAGlB,CAAO,KAAKkB,CAAS,GACrDlB,GACAkB,GAEG,aACT,CAMA,IAAMC,GAA2B,uFAC3BC,GAA8B,mFAC9BC,GAA2B,sCAEjC,SAAS3B,GAAiBV,EAAwC,CAChE,IAAMS,EAA+B,CAAC,EAiBtC,GAdIT,EAAK,QAAQ,iBAAsB,SACrCS,EAAY,aAAeT,EAAK,QAAQ,iBAAsB,SAE5DA,EAAK,QAAQ,oBAAyB,SACxCS,EAAY,gBAAkBT,EAAK,QAAQ,oBAAyB,SAElEA,EAAK,QAAQ,mBAAwB,SACvCS,EAAY,eAAiBT,EAAK,QAAQ,mBAAwB,SAEhEA,EAAK,QAAQ,kBAAuB,SACtCS,EAAY,cAAgBT,EAAK,QAAQ,kBAAuB,SAI9DS,EAAY,eAAiB,OAAW,CAC1C,IAAM6B,EAAQtC,EAAK,OAAO,YAAY,IAAM,MACtCc,EAAaC,GAAoBf,CAAI,EACrCuC,EAAczB,EAAaqB,GAAyB,KAAKrB,EAAW,KAAK,CAAC,EAAI,IAChFwB,GAASC,KAAa9B,EAAY,aAAe,GACvD,CAGA,GAAIA,EAAY,kBAAoB,OAAW,CAC7C,IAAMK,EAAaC,GAAoBf,CAAI,EACrCwC,EAAqB1B,EAAasB,GAA4B,KAAKtB,EAAW,KAAK,CAAC,EAAI,GACxF2B,EAAmBzC,EAAK,OAASqC,GAAyB,KAAKrC,EAAK,MAAM,EAAI,IAChFwC,GAAsBC,KAAkBhC,EAAY,gBAAkB,GAC5E,CAGA,OAAIA,EAAY,iBAAmB,SAC7BA,EAAY,eAAiB,IAAQT,EAAK,OAAO,YAAY,IAAM,SACrES,EAAY,eAAiB,IAK7BA,EAAY,gBAAkB,SAChCA,EAAY,cAAgBA,EAAY,eAAiB,IAKzDA,EAAY,eAAiB,IAC7BA,EAAY,kBAAoB,IAChCA,EAAY,iBAAmB,IAC/BA,EAAY,gBAAkB,GAETA,EAAc,CAAC,CACxC,CAWA,SAASiC,GACPC,EACS,CACT,GAAIA,aAAmB,iBAAkB,CACvC,IAAMC,EAAOD,EAAQ,KAAK,YAAY,EACtC,OAAIC,IAAS,WAAmBD,EAAQ,QAAU,GAAO,OACrDC,IAAS,QAAS,OAClBA,IAAS,UAAYA,IAAS,QACzBD,EAAQ,QAAU,GAAK,WAAWA,EAAQ,KAAK,EAAI,OAErDA,EAAQ,QAAU,GAAKA,EAAQ,MAAQ,MAChD,CACA,GAAIA,aAAmB,oBACrB,OAAOA,EAAQ,QAAU,GAAKA,EAAQ,MAAQ,OAEhD,GAAIA,aAAmB,kBAAmB,CACxC,GAAIA,EAAQ,SAAU,CACpB,IAAME,EAAW,MAAM,KAAKF,EAAQ,OAAO,EAAE,OAAQG,GAAMA,EAAE,QAAQ,EAAE,IAAKA,GAAMA,EAAE,KAAK,EACzF,OAAOD,EAAS,OAAS,EAAIA,EAAW,MAC1C,CACA,OAAOF,EAAQ,QAAU,GAAKA,EAAQ,MAAQ,MAChD,CAEF,CASA,SAASI,GACPC,EACAC,EAAU,IAAI,IACqD,CACnE,GAAIA,EAAQ,IAAID,CAAI,EAAG,MAAO,CAAC,EAC/BC,EAAQ,IAAID,CAAI,EAChB,IAAME,EAA6E,CAAC,EACpF,QAAWC,KAAM,MAAM,KAAKH,EAAK,iBAAiB,GAAG,CAAC,EACpD,GAAIG,EAAG,WAAY,CACjB,IAAMC,EAAQ,MAAM,KAClBD,EAAG,WAAW,iBACZ,yBACF,CACF,EACIC,EAAM,OAAS,GACjB,QAAQ,IAAI,+BAA+BA,EAAM,MAAM,kBAAkBD,EAAG,QAAQ,YAAY,CAAC,gBAAiBC,EAAM,IAAIC,GAAK,GAAGA,EAAE,QAAQ,YAAY,CAAC,SAASA,EAAE,MAAQ,GAAG,WAAYA,EAAuB,IAAI,UAAUA,EAAE,EAAE,IAAI,CAAC,EAE7OH,EAAQ,KAAK,GAAGE,EAAO,GAAGL,GAAsBI,EAAG,WAAYF,CAAO,CAAC,CACzE,CAEF,OAAOC,CACT,CAOA,SAASI,GACPtD,EACmE,CACnE,IAAMuD,EAAW,MAAM,KAAKvD,EAAK,QAAQ,EAAE,OACxCmD,GACCA,aAAc,kBACdA,aAAc,qBACdA,aAAc,iBAClB,EAEMK,EAAO,IAAI,IAAaD,CAAQ,EACtC,QAAWE,KAAiBV,GAAsB/C,CAAI,EAC/CwD,EAAK,IAAIC,CAAa,IACzBF,EAAS,KAAKE,CAAa,EAC3BD,EAAK,IAAIC,CAAa,GAI1B,OAAOF,CACT,CAEA,SAAS/C,GAAYR,EAAoF,CACvG,IAAM0D,EAAiD,CAAC,EAClDC,EAAqB,CAAC,EACtBpD,EAAgB,IAAI,IAGpBqD,EAAuB,IAAI,IAC3BC,EAA0B,IAAI,IAE9BN,EAAWD,GAA8BtD,CAAI,EAEnD,QAAW2C,KAAWY,EAAU,CAC9B,IAAMrD,EAAOyC,EAAQ,KACfmB,EAAW5D,GAAQ6D,GAAgCpB,CAAO,EAChE,GAAI,CAACmB,EAAU,SAGf,GAAInB,aAAmB,kBAAoBA,EAAQ,OAAS,QAAS,CACnE,GAAIiB,EAAqB,IAAIE,CAAQ,EAAG,SACxCF,EAAqB,IAAIE,CAAQ,CACnC,CAGA,GAAInB,aAAmB,kBAAoBA,EAAQ,OAAS,WAAY,CACtE,GAAIkB,EAAwB,IAAIC,CAAQ,EAAG,SAC3CD,EAAwB,IAAIC,CAAQ,CACtC,CAEA,IAAME,EAAaC,GAAkBtB,CAAO,EAE5C,GADI,CAACqB,GACD,CAACE,GAAiBvB,CAAO,EAAG,SAGhCqB,EAAW,MAAQG,GAAgBxB,CAAO,EAC1C,IAAMyB,EAAOC,GAAsB1B,CAAO,EACtCyB,IAAMJ,EAAW,YAAcI,GAGnC,IAAME,EAAa5B,GAAoBC,CAAO,EAI9C,GAHI2B,IAAe,SAAWN,EAAW,QAAUM,GAG/C3B,aAAmB,kBAAoBA,EAAQ,OAAS,QAAS,CACnEqB,EAAW,KAAOO,GAAiBvE,EAAM8D,CAAQ,EACjD,IAAMU,EAAaC,GAAkBzE,EAAM8D,CAAQ,EAC/CU,EAAW,OAAS,IAAGR,EAAW,MAAQQ,GAC9C,IAAME,EAAe,MAAM,KAAK1E,EAAK,QAAQ,EAAE,KAC5CmD,GACCA,aAAc,kBACdA,EAAG,OAAS,SACZA,EAAG,OAASW,GACZX,EAAG,OACP,EACIuB,GAAc,QAAOV,EAAW,QAAUU,EAAa,MAC7D,CAGA,GAAI/B,aAAmB,kBAAoBA,EAAQ,OAAS,WAAY,CACtE,IAAMgC,EAAiBC,GAAoB5E,EAAM8D,CAAQ,EACzD,GAAIa,EAAe,OAAS,EAAG,CAC7B,IAAME,EAAgC,CACpC,KAAM,QACN,MAAO,CAAE,KAAM,SAAU,KAAMF,CAAe,EAC9C,MAAOX,EAAW,KACpB,EACIA,EAAW,cAAaa,EAAU,YAAcb,EAAW,aAC/D,IAAMc,EAAe,MAAM,KAAK9E,EAAK,QAAQ,EAC1C,OACEmD,GACCA,aAAc,kBACdA,EAAG,OAAS,YACZA,EAAG,OAASW,GACZX,EAAG,OACP,EACC,IAAK4B,GAAMA,EAAE,KAAK,EACjBD,EAAa,OAAS,IAAGD,EAAU,QAAUC,GACjDpB,EAAWI,CAAQ,EAAIe,EACnBlC,EAAQ,UAAUgB,EAAS,KAAKG,CAAQ,EAC5C,QACF,CACF,CAEAJ,EAAWI,CAAQ,EAAIE,EAGlB9D,GACHK,EAAc,IAAIuD,EAAUnB,CAAO,EAOrC,IAAIqC,EAAarC,EAAQ,SACzB,GAAI,CAACqC,EAAY,CACf,IAAIC,EAAoBtC,EACxB,OAAa,CACX,IAAMK,EAAOiC,EAAS,YAAY,EAClC,GAAI,EAAEjC,aAAgB,YAAa,MACnC,IAAMkC,EAAOlC,EAAK,KAClB,GAAIkC,EAAK,aAAa,UAAU,GAAKA,EAAK,aAAa,eAAe,IAAM,OAAQ,CAClFF,EAAa,GACb,KACF,CACAC,EAAWC,CACb,CACF,CACIF,GAAYrB,EAAS,KAAKG,CAAQ,CACxC,CAGA,IAAMqB,EAAeC,GAAoBpF,CAAI,EACvCqF,EAA2B,IAAI,IAErC,OAAW,CAAE,GAAAlC,EAAI,KAAAmC,EAAM,IAAAC,EAAK,WAAAC,EAAY,UAAAC,CAAU,IAAKN,EAAc,CACnE,GAAIzB,EAAW6B,CAAG,EAAG,SAErB,GAAID,IAAS,QAAS,CACpB,GAAID,EAAyB,IAAIE,CAAG,EAAG,SACvCF,EAAyB,IAAIE,CAAG,CAClC,CAEA,IAAMvB,EAAa0B,GAAiBvC,EAAImC,CAAI,EAGxCE,GAAcA,EAAW,OAAS,IACpCxB,EAAW,KAAOwB,EACdC,GAAaA,EAAU,OAAS,IAAGzB,EAAW,MAAQyB,IAG5DzB,EAAW,MAAQ2B,GAAoBxC,CAAE,EACzC,IAAMiB,EAAOwB,GAA0BzC,CAAE,EACrCiB,IAAMJ,EAAW,YAAcI,GAEnCV,EAAW6B,CAAG,EAAIvB,EAClBzD,EAAc,IAAIgF,EAAKpC,CAAE,EAErBA,EAAG,aAAa,eAAe,IAAM,QACvCQ,EAAS,KAAK4B,CAAG,CAErB,CAEA,MAAO,CAAE,OAAQ,CAAE,QAAW,+CAAgD,KAAM,SAAU,WAAA7B,EAAY,SAAAC,CAAS,EAAG,cAAApD,CAAc,CACtI,CAGA,IAAMsF,GAAuB,iCAG7B,SAAS9B,GACPpB,EACe,CACf,IAAMQ,EAAKR,EACX,GAAIQ,EAAG,QAAQ,WAAe,OAAOvC,EAAauC,EAAG,QAAQ,UAAc,EAG3E,GAAIR,EAAQ,IAAM,CAACkD,GAAqB,KAAKlD,EAAQ,EAAE,EAAG,OAAO/B,EAAa+B,EAAQ,EAAE,EACxF,IAAMmD,EAAQnD,EAAQ,aAAa,YAAY,EAC/C,GAAImD,EAAO,OAAOlF,EAAakF,CAAK,EAGpC,IACGnD,aAAmB,kBAAoBA,aAAmB,sBAC3DA,EAAQ,aAAa,KAAK,EAE1B,OAAO/B,EAAa+B,EAAQ,YAAY,KAAK,CAAC,EAKhD,IAAMoD,EAAUC,GAAqBrD,CAAO,EAC5C,OAAIoD,IAEApD,aAAmB,kBAAoBA,EAAQ,OAAS,OACnDA,EAAQ,KAEV,KACT,CASA,SAASqD,GAAqB7C,EAA4B,CACxD,IAAI1B,EAAgB0B,EACpB,OAAa,CACX,IAAMH,EAAOvB,EAAK,YAAY,EAC9B,GAAI,EAAEuB,aAAgB,YAAa,MACnC,IAAMkC,EAAOlC,EAAK,KACZiD,EAAYf,EAAK,aAAa,YAAY,EAChD,GAAIe,EAAa,eAAQ,IAAI,6CAA8CA,CAAS,EAAUrF,EAAaqF,CAAS,EACpH,IAAMC,EAAYhB,EAAK,aAAa,OAAO,GAAKA,EAAK,aAAa,YAAY,EAC9E,GAAIgB,EAAa,eAAQ,IAAI,wCAAyCA,CAAS,EAAUtF,EAAasF,CAAS,EAC/G,IAAMC,EAAWjB,EAAK,aAAa,MAAM,EACzC,GAAIiB,EAAY,eAAQ,IAAI,uCAAwCA,CAAQ,EAAUvF,EAAauF,CAAQ,EAC3G1E,EAAOyD,CACT,CACA,OAAO,IACT,CAGA,SAASkB,GAAsBjD,EAA4B,CACzD,GAAKA,EAAmB,QAAQ,WAAe,OAAOvC,EAAcuC,EAAmB,QAAQ,UAAc,EAC7G,GAAIA,EAAG,IAAM,CAAC0C,GAAqB,KAAK1C,EAAG,EAAE,EAAG,OAAOvC,EAAauC,EAAG,EAAE,EACzE,IAAM2C,EAAQ3C,EAAG,aAAa,YAAY,EAC1C,GAAI2C,EAAO,OAAOlF,EAAakF,CAAK,EACpC,IAAMO,EAAclD,EAAG,aAAa,aAAa,EACjD,OAAIkD,EAAoBzF,EAAayF,CAAW,EACzC,IACT,CAaA,SAASjB,GAAoBpF,EAAgD,CAC3E,IAAMsG,EAAWC,EAAmB,IAAKC,GAAM,UAAUA,CAAC,IAAI,EAAE,KAAK,IAAI,EACnEC,EAAkE,CAAC,EAEzE,QAAWtD,KAAM,MAAM,KAAKnD,EAAK,iBAAiBsG,CAAQ,CAAC,EAAG,CAS5D,GANEnD,aAAc,kBACdA,aAAc,qBACdA,aAAc,mBAIZA,EAAG,aAAa,aAAa,IAAM,QAAWA,EAAmB,OAAQ,SAE7E,IAAMmC,EAAOnC,EAAG,aAAa,MAAM,EAC7BoC,EAAMmB,GAAoBvD,CAAE,EAC7BoC,GAELkB,EAAW,KAAK,CAAE,GAAAtD,EAAI,KAAAmC,EAAM,IAAAC,CAAI,CAAC,CACnC,CAGA,IAAMoB,EAAeF,EAAW,OAAQG,GAAMA,EAAE,OAAS,OAAO,EAC1DC,EAAsCJ,EAAW,OAAQG,GAAMA,EAAE,OAAS,OAAO,EAEjFE,EAAgB,IAAI,IACpBC,EAAsC,CAAC,EAE7C,QAAWC,KAASL,EAAc,CAChC,IAAMM,EAAQD,EAAM,GAAG,QAAQ,qBAAqB,EAChDC,GACGH,EAAc,IAAIG,CAAK,GAAGH,EAAc,IAAIG,EAAO,CAAC,CAAC,EAC1DH,EAAc,IAAIG,CAAK,EAAG,KAAKD,EAAM,EAAE,GAEvCD,EAAgB,KAAKC,CAAK,CAE9B,CAEA,IAAME,EAAqC,CAAC,EAC5C,OAAW,CAACD,EAAOE,CAAO,IAAKL,EAAe,CAC5C,IAAMM,EAAWV,GAAoBO,CAAK,EAC1C,GAAI,CAACG,EAAU,SACf,IAAM5B,EAAa2B,EAChB,IAAKhE,IAAQA,EAAG,aAAa,YAAY,GAAKA,EAAG,aAAa,YAAY,GAAKA,EAAG,aAAe,IAAI,KAAK,CAAC,EAC3G,OAAO,OAAO,EACXsC,EAAY0B,EACf,IAAKhE,GAAO,CACX,IAAMkE,GAAOlE,EAAG,aAAa,YAAY,GAAKA,EAAG,aAAa,YAAY,GAAKA,EAAG,aAAe,IAAI,KAAK,EACpGmE,GAASnE,EAAG,aAAa,YAAY,GAAKA,EAAG,aAAe,IAAI,KAAK,EAC3E,MAAO,CAAE,MAAOkE,EAAK,MAAOC,GAASD,CAAI,CAC3C,CAAC,EACA,OAAQT,GAAMA,EAAE,QAAU,EAAE,EAC3BpB,EAAW,OAAS,GACtB0B,EAAe,KAAK,CAAE,GAAID,EAAO,KAAM,QAAS,IAAKG,EAAU,WAAA5B,EAAY,UAAAC,CAAU,CAAC,CAE1F,CAEA,MAAO,CAAC,GAAGoB,EAAiB,GAAGK,EAAgB,GAAGH,CAAe,CACnE,CAGA,SAASL,GAAoBvD,EAA4B,CACvD,IAAMoE,EAASpE,EACf,GAAIoE,EAAO,SAAU,WAAe,OAAO3G,EAAa2G,EAAO,QAAQ,UAAc,EACrF,GAAIpE,EAAG,GAAI,OAAOvC,EAAauC,EAAG,EAAE,EACpC,IAAM2C,EAAQ3C,EAAG,aAAa,YAAY,EAC1C,GAAI2C,EAAO,OAAOlF,EAAakF,CAAK,EACpC,IAAM0B,EAAerE,EAAG,aAAa,iBAAiB,EACtD,GAAIqE,EAAc,CAChB,IAAMhG,EAAO,SAAS,eAAegG,CAAY,GAAG,aAAa,KAAK,EACtE,GAAIhG,EAAM,OAAOZ,EAAaY,CAAI,CACpC,CACA,OAAO,IACT,CAEA,SAASmE,GAAoBxC,EAAqB,CAChD,IAAMoE,EAASpE,EACf,GAAIoE,EAAO,SAAU,YAAgB,OAAOA,EAAO,QAAQ,YAC3D,IAAMzB,EAAQ3C,EAAG,aAAa,YAAY,EAC1C,GAAI2C,EAAO,OAAOA,EAAM,KAAK,EAC7B,IAAM0B,EAAerE,EAAG,aAAa,iBAAiB,EACtD,GAAIqE,EAAc,CAChB,IAAMhG,EAAO,SAAS,eAAegG,CAAY,GAAG,aAAa,KAAK,EACtE,GAAIhG,EAAM,OAAOA,CACnB,CACA,OAAI2B,EAAG,GAAWsE,GAAatE,EAAG,EAAE,EAC7B,EACT,CAEA,SAASyC,GAA0BzC,EAAqB,CACtD,IAAMuE,EAAkBvE,EAAG,aAAa,sBAAsB,EAC9D,GAAIuE,EAAiB,OAAOA,EAAgB,KAAK,EACjD,IAAMH,EAASpE,EACf,GAAIoE,EAAO,SAAU,kBAAsB,OAAOA,EAAO,QAAQ,kBACjE,IAAMI,EAAWxE,EAAG,aAAa,kBAAkB,EACnD,GAAIwE,EAAU,OAAOA,EACrB,IAAM3F,EAAgBmB,EAAG,aAAa,kBAAkB,EACxD,GAAInB,EAAe,CACjB,IAAMR,EAAO,SAAS,eAAeQ,CAAa,GAAG,aAAa,KAAK,EACvE,GAAIR,EAAM,OAAOA,CACnB,CACA,IAAM6E,EAAclD,EAAG,aAAa,aAAa,GAAMA,EAAmB,SAAU,YACpF,OAAIkD,EAAoBA,EAAY,KAAK,EAClC,EACT,CAEA,SAASlC,GACPxB,EACQ,CAER,GAAI,YAAaA,GAAYA,EAAwB,QAAQ,YAC3D,OAAQA,EAAwB,QAAQ,YAI1C,IAAMiF,EAAYC,GAAuBlF,CAAO,EAChD,OAAIiF,IAGAjF,EAAQ,KAAa8E,GAAa9E,EAAQ,IAAI,EAG9CA,EAAQ,GAAW8E,GAAa9E,EAAQ,EAAE,GAI3CA,aAAmB,kBAAoBA,aAAmB,sBAC3DA,EAAQ,aAAa,KAAK,EAEnBA,EAAQ,YAAY,KAAK,EAG3B,GACT,CAEA,SAAS0B,GACP1B,EACQ,CAER,IAAM+E,EAAkB/E,EAAQ,aAAa,sBAAsB,EACnE,GAAI+E,EAAiB,OAAOA,EAAgB,KAAK,EAGjD,IAAMvE,EAAKR,EACX,GAAIQ,EAAG,QAAQ,kBAAsB,OAAOA,EAAG,QAAQ,kBAGvD,IAAMwE,EAAWhF,EAAQ,aAAa,kBAAkB,EACxD,GAAIgF,EAAU,OAAOA,EAErB,IAAM3F,EAAgBW,EAAQ,aAAa,kBAAkB,EAC7D,GAAIX,EAAe,CACjB,IAAMC,EAAS,SAAS,eAAeD,CAAa,EACpD,GAAIC,GAAQ,aAAa,KAAK,EAAG,OAAOA,EAAO,YAAY,KAAK,CAClE,CAGA,GAAIU,aAAmB,kBAAoBA,aAAmB,oBAAqB,CACjF,IAAMmF,EAAKnF,EAAQ,aAAa,KAAK,EACrC,GAAImF,GAAMA,EAAG,OAAS,EAAG,OAAOA,CAClC,CAGA,MAAO,EACT,CAEA,SAASD,GACPlF,EACQ,CAER,GAAIA,EAAQ,GAAI,CACd,IAAMmD,EAAQ,SAAS,cAAgC,cAAc,IAAI,OAAOnD,EAAQ,EAAE,CAAC,IAAI,EAC/F,GAAImD,EAAO,CACT,IAAMtE,EAAOuG,EAAuBjC,CAAK,EACzC,GAAItE,EAAM,OAAOA,CACnB,CACF,CAIA,IAAMwG,EAAUrF,EAAQ,YAAY,EACpC,GAAIqF,aAAmB,WAAY,CACjC,GAAIrF,EAAQ,GAAI,CACd,IAAMsF,EAAcD,EAAQ,cAAgC,cAAc,IAAI,OAAOrF,EAAQ,EAAE,CAAC,IAAI,EACpG,GAAIsF,EAAa,CACf,IAAMzG,EAAOuG,EAAuBE,CAAW,EAC/C,GAAIzG,EAAM,OAAOA,CACnB,CACF,CAEA,IAAM0G,EAAWF,EAAQ,cAAgC,OAAO,EAChE,GAAIE,EAAU,CACZ,IAAM1G,EAAOuG,EAAuBG,CAAQ,EAC5C,GAAI1G,EAAM,OAAOA,CACnB,CACF,CAGA,IAAM2G,EAASxF,EAAQ,QAAQ,OAAO,EACtC,GAAIwF,EAAQ,CACV,IAAM3G,EAAOuG,EAAuBI,CAAM,EAC1C,GAAI3G,EAAM,OAAOA,CACnB,CAGA,IAAIC,EAAgBkB,EACpB,OAAa,CACX,IAAMK,EAAOvB,EAAK,YAAY,EAC9B,GAAI,EAAEuB,aAAgB,YAAa,MACnC,IAAMkC,EAAOlC,EAAK,KACZkD,EAAYhB,EAAK,aAAa,OAAO,GAAKA,EAAK,aAAa,YAAY,EAC9E,GAAIgB,EAAW,OAAOA,EACtBzE,EAAOyD,CACT,CAEA,MAAO,EACT,CAEA,SAAS6C,EAAuBjC,EAAiC,CAE/D,IAAMsC,EAAQtC,EAAM,UAAU,EAAI,EAClC,OAAAsC,EAAM,iBAAiB,iCAAiC,EAAE,QAASjF,GAAOA,EAAG,OAAO,CAAC,EAC9EiF,EAAM,aAAa,KAAK,GAAK,EACtC,CAEA,SAASX,GAAapG,EAAqB,CACzC,OAAOA,EACJ,QAAQ,QAAS,GAAG,EACpB,QAAQ,kBAAmB,OAAO,EAClC,KAAK,EACL,QAAQ,QAAUgH,GAAMA,EAAE,YAAY,CAAC,CAC5C,CAWA,SAASnE,GAAiBf,EAA0B,CAClD,IAAMmF,EAAQ,OAAO,iBAAiBnF,CAAE,EAIxC,GAHImF,EAAM,UAAY,QAClBA,EAAM,aAAe,UAErBnF,EAAG,eAAiB,MAAQmF,EAAM,WAAa,QAAS,MAAO,GAGnE,IAAI7G,EAAuB0B,EAC3B,KAAO1B,GAAQA,IAAS,SAAS,MAAM,CACrC,GAAIA,EAAK,aAAa,aAAa,IAAM,OAAQ,MAAO,GACxDA,EAAOA,EAAK,aACd,CAGA,MAAI,CAAA0B,EAAG,QAAQ,UAAU,GAAG,QAG9B,CAUO,SAASoF,GACdC,EACAC,EACAC,EACc,CACd,IAAMxI,EAAOyI,GAAoBH,EAAWE,CAAS,EAC/CtI,EAAcwI,GAA2BJ,CAAS,EAClD,CAAE,OAAQlI,EAAa,cAAAC,CAAc,EAAIsI,GAAsBJ,CAAM,EACrEhI,EAAcqI,GAAuBJ,CAAS,EACpD,MAAO,CAAE,KAAAxI,EAAM,YAAAE,EAAa,YAAAE,EAAa,YAAAG,EAAa,cAAAF,CAAc,CACtE,CAEA,SAASuI,GACPJ,EACiB,CACjB,IAAMjI,EAA+B,CAAC,EAChCK,EACJ4H,aAAqB,iBACjBA,EAAU,MAAM,KAAK,EACpBA,GAAW,aAAa,KAAK,GAAK,GAEzC,OAAIvG,GAAyB,KAAKrB,CAAU,IAC1CL,EAAY,aAAe,GAC3BA,EAAY,eAAiB,IAE3B2B,GAA4B,KAAKtB,CAAU,IAC7CL,EAAY,gBAAkB,IAE5BA,EAAY,eAAiB,KAC/BA,EAAY,cAAgB,IAI5BA,EAAY,eAAiB,IAC7BA,EAAY,kBAAoB,IAChCA,EAAY,iBAAmB,IAC/BA,EAAY,gBAAkB,GAETA,EAAc,CAAC,CACxC,CAEA,SAASkI,GACPH,EACAE,EACQ,CAER,GAAIA,EAAW,CACb,IAAMlH,EACJkH,aAAqB,iBACjBA,EAAU,MAAM,KAAK,EACrBA,EAAU,aAAa,KAAK,GAAK,GACvC,GAAIlH,GAAQA,EAAK,OAAS,GAAKA,EAAK,OAAS,GAAI,OAAOZ,EAAaY,CAAI,CAC3E,CAGA,IAAMR,EAAU+H,GAA0BP,CAAS,EACnD,GAAIxH,EAAS,OAAOJ,EAAaI,CAAO,EAGxC,IAAMsG,EAAQ,SAAS,OAAO,KAAK,EACnC,OAAIA,EAAc1G,EAAa0G,CAAK,EAE7B,QAAQ,EAAElG,EAAS,EAC5B,CAEA,SAASwH,GAA2BJ,EAA4B,CAE9D,IAAMxH,EAAU+H,GAA0BP,CAAS,EAC7CtG,EAAY,SAAS,OAAO,KAAK,EACvC,OAAIlB,GAAWkB,GAAalB,IAAYkB,EAAkB,GAAGlB,CAAO,OAAOkB,CAAS,GAChFlB,GACAkB,GACG,aACT,CAMA,SAAS6G,GAA0B5F,EAAqB,CAEtD,IAAM6F,EAAQ7F,EAAG,cAAc,YAAY,EAC3C,GAAI6F,GAAO,aAAa,KAAK,EAAG,OAAOA,EAAM,YAAY,KAAK,EAE9D,IAAIvH,EAAuB0B,EAC3B,KAAO1B,GAAM,CACX,IAAIC,EAAUD,EAAK,uBACnB,KAAOC,GAAS,CACd,GAAI,YAAY,KAAKA,EAAQ,OAAO,EAAG,CACrC,IAAMF,EAAOE,EAAQ,aAAa,KAAK,GAAK,GAC5C,GAAIF,EAAM,OAAOA,CACnB,CACAE,EAAUA,EAAQ,sBACpB,CAEA,GADAD,EAAOA,EAAK,cACR,CAACA,GAAQA,IAAS,SAAS,KAAM,KACvC,CACA,MAAO,EACT,CAMA,SAASoH,GACPJ,EAC6D,CAC7D,IAAM/E,EAAiD,CAAC,EAClDC,EAAqB,CAAC,EACtBpD,EAAgB,IAAI,IACpBqD,EAAuB,IAAI,IAC3BC,EAA0B,IAAI,IAEpC,QAAWlB,KAAW8F,EAAQ,CAE5B,GAAI,EAAE9F,aAAmB,mBAAqB,EAAEA,aAAmB,sBAAwB,EAAEA,aAAmB,mBAAoB,CAClI,IAAMmB,EAAWsC,GAAsBzD,CAAO,EAE9C,GADI,CAACmB,GACD,CAACI,GAAiBvB,CAAO,EAAG,SAChC,IAAMsG,EAA2B,CAAE,KAAM,QAAS,EAClDA,EAAK,MAAQtG,EAAQ,aAAa,YAAY,GAAKmB,EACnD,IAAMM,GAAOzB,EAAQ,aAAa,kBAAkB,GAAKA,EAAQ,aAAa,kBAAkB,EAAI,MAChGyB,IAAM6E,EAAK,YAAc7E,GAC7BV,EAAWI,CAAQ,EAAImF,EACvB1I,EAAc,IAAIuD,EAAUnB,CAAO,EACnCgB,EAAS,KAAKG,CAAQ,EACtB,QACF,CAEA,IAAMoF,EAAWvG,EAAuE,KAGlFmB,GAAYoF,EAAUtI,EAAasI,CAAO,EAAI,OAASnF,GAAgCpB,CAAO,EACpG,GAAI,CAACmB,EAAU,SAEf,GAAInB,aAAmB,kBAAoBA,EAAQ,OAAS,QAAS,CACnE,GAAIiB,EAAqB,IAAIE,CAAQ,EAAG,SACxCF,EAAqB,IAAIE,CAAQ,CACnC,CAEA,GAAInB,aAAmB,kBAAoBA,EAAQ,OAAS,WAAY,CACtE,GAAIkB,EAAwB,IAAIC,CAAQ,EAAG,SAC3CD,EAAwB,IAAIC,CAAQ,CACtC,CAEA,IAAME,EAAaC,GAAkBtB,CAAO,EAE5C,GADI,CAACqB,GACD,CAACE,GAAiBvB,CAAO,EAAG,SAEhCqB,EAAW,MAAQG,GAAgBxB,CAAO,EAC1C,IAAMyB,EAAOC,GAAsB1B,CAAO,EAI1C,GAHIyB,IAAMJ,EAAW,YAAcI,GAG/BzB,aAAmB,kBAAoBA,EAAQ,OAAS,WAAY,CACtE,IAAMgC,EAAiB8D,EACpB,OAAQU,GAA6BA,aAAa,kBAAoBA,EAAE,OAAS,YAAcA,EAAE,OAASrF,CAAQ,EAClH,IAAKsF,GAAOA,EAAG,KAAK,EACpB,OAAQC,GAAMA,IAAM,IAAMA,IAAM,IAAI,EACvC,GAAI1E,EAAe,OAAS,EAAG,CAC7B,IAAME,EAAgC,CACpC,KAAM,QACN,MAAO,CAAE,KAAM,SAAU,KAAMF,CAAe,EAC9C,MAAOX,EAAW,KACpB,EACIA,EAAW,cAAaa,EAAU,YAAcb,EAAW,aAC/DN,EAAWI,CAAQ,EAAIe,EACnBlC,EAAQ,UAAUgB,EAAS,KAAKG,CAAQ,EAC5C,QACF,CACF,CAEAJ,EAAWI,CAAQ,EAAIE,EAClBkF,GAAS3I,EAAc,IAAIuD,EAAUnB,CAAO,EAC7CA,EAAQ,UAAUgB,EAAS,KAAKG,CAAQ,CAC9C,CAEA,MAAO,CAAE,OAAQ,CAAE,QAAW,+CAAgD,KAAM,SAAU,WAAAJ,EAAY,SAAAC,CAAS,EAAG,cAAApD,CAAc,CACtI,CCx8BA,IAAM+I,GAAgC,CACpC,KAAM,SACN,WAAY,CACV,OAAQ,CACN,KAAM,SACN,KAAM,CAAC,UAAW,UAAW,QAAS,uBAAwB,YAAa,iBAAiB,EAC5F,YAAa,gCACf,EACA,cAAe,CACb,KAAM,SACN,YAAa,oCACf,EACA,eAAgB,CACd,KAAM,QACN,MAAO,CAAE,KAAM,QAAS,EACxB,YAAa,yDACf,EACA,iBAAkB,CAChB,KAAM,QACN,MAAO,CAAE,KAAM,QAAS,EACxB,YAAa,4CACf,EACA,kBAAmB,CACjB,KAAM,QACN,MAAO,CACL,KAAM,SACN,WAAY,CACV,MAAO,CAAE,KAAM,QAAS,EACxB,WAAY,CAAE,KAAM,SAAU,YAAa,qCAAsC,EACjF,QAAS,CAAE,KAAM,QAAS,CAC5B,EACA,SAAU,CAAC,QAAS,aAAc,SAAS,CAC7C,EACA,YAAa,+EACf,EACA,gBAAiB,CACf,KAAM,SACN,YAAa,8DACf,EACA,SAAU,CACR,KAAM,QACN,MAAO,CAAE,KAAM,QAAS,EACxB,YAAa,sEACf,CACF,EACA,SAAU,CAAC,SAAU,gBAAiB,iBAAkB,mBAAoB,UAAU,CACxF,EAoBMC,EAAkB,IAAI,IAEtBC,GAA0B,IAAI,IAG7B,SAASC,GAA6B,CAC3C,OAAO,OAAO,UAAc,KAAe,OAAO,UAAU,aAAiB,GAC/E,CAMA,eAAsBC,GACpBC,EACAC,EACAC,EACe,CACf,GAAI,CAACJ,EAAkB,EAAG,OAGTF,EAAgB,IAAII,CAAI,GAEvC,MAAMG,EAAmBH,CAAI,EAG/B,IAAMI,EAAsB,CAC1B,KAAMH,EAAS,KACf,YAAaA,EAAS,YACtB,YAAaA,EAAS,YACtB,aAAcN,GACd,QAAAO,CACF,EACID,EAAS,aAAe,OAAO,KAAKA,EAAS,WAAW,EAAE,OAAS,IACrEG,EAAQ,YAAcH,EAAS,aAGjC,IAAMI,EAAa,IAAI,gBACvBR,GAAwB,IAAIG,EAAMK,CAAU,EAE5C,GAAI,CACF,MAAM,UAAU,aAAc,aAAaD,EAAS,CAAE,OAAQC,EAAW,MAAO,CAAC,CACnF,MAAQ,CAGN,GAAI,CACF,MAAM,UAAU,aAAc,iBAAiBJ,EAAS,IAAI,EAC5D,MAAM,UAAU,aAAc,aAAaG,EAAS,CAAE,OAAQC,EAAW,MAAO,CAAC,CACnF,MAAQ,CAER,CACF,CAEAT,EAAgB,IAAII,EAAMC,EAAS,IAAI,CACzC,CAMA,eAAsBE,EAAmBH,EAAsC,CAC7E,GAAI,CAACF,EAAkB,EAAG,OAE1B,IAAMQ,EAAOV,EAAgB,IAAII,CAAI,EACrC,GAAI,CAACM,EAAM,OAEX,IAAMD,EAAaR,GAAwB,IAAIG,CAAI,EAC/CK,IACFA,EAAW,MAAM,EACjBR,GAAwB,OAAOG,CAAI,GAGrC,GAAI,CACF,MAAM,UAAU,aAAc,iBAAiBM,CAAI,CACrD,MAAQ,CAER,CAEAV,EAAgB,OAAOI,CAAI,CAC7B,CAGO,SAASO,GAAsBP,EAA2C,CAC/E,OAAOJ,EAAgB,IAAII,CAAI,CACjC,CAGO,SAASQ,GAAwE,CACtF,OAAO,MAAM,KAAKZ,EAAgB,QAAQ,CAAC,EAAE,IAAI,CAAC,CAACI,EAAMM,CAAI,KAAO,CAAE,KAAAN,EAAM,KAAAM,CAAK,EAAE,CACrF,CAGA,eAAsBG,IAA+B,CACnD,IAAMC,EAAU,MAAM,KAAKd,EAAgB,QAAQ,CAAC,EACpD,MAAM,QAAQ,IAAIc,EAAQ,IAAI,CAAC,CAACV,CAAI,IAAMG,EAAmBH,CAAI,CAAC,CAAC,CACrE,CChHA,IAAMW,EAAoB,IAAI,QAMxBC,EAAa,IAAI,QAGjBC,EAAoB,IAAI,QAGxBC,EAAkB,IAAI,QAGtBC,EAAsB,IAAI,QAQ1BC,EAAqB,IAAI,QAMzBC,EAAgB,IAAI,QAIpBC,GACJ,OAAO,yBAAyB,iBAAiB,UAAW,OAAO,GAAG,IAClEC,GACJ,OAAO,yBAAyB,oBAAoB,UAAW,OAAO,GAAG,IACrEC,EACJ,OAAO,yBAAyB,iBAAiB,UAAW,SAAS,GAAG,IAE1E,SAASC,GAAkBC,EAAqB,CAC9C,OAAOA,EAAI,YAAY,EAAE,QAAQ,cAAe,EAAE,CACpD,CAEA,SAASC,EACPC,EACAC,EACAC,EACM,CACN,GAAI,CAACD,EAAO,OACZ,IAAME,EAAaN,GAAkBI,CAAK,EACrCE,IACAH,EAAM,IAAIG,CAAU,GAAGH,EAAM,IAAIG,EAAY,IAAI,GAAa,EACnEH,EAAM,IAAIG,CAAU,EAAG,IAAID,CAAS,EACtC,CAEA,SAASE,GACPC,EACAC,EAC0B,CAC1B,IAAMN,EAAQ,IAAI,IACZO,EAAaD,GAAU,aAAa,YAAc,CAAC,EAEzD,OAAW,CAACJ,EAAWM,CAAI,IAAK,OAAO,QAAQD,CAAU,EAAG,CAC1DR,EAASC,EAAOE,EAAWA,CAAS,EACpCH,EAASC,EAAOE,EAAU,QAAQ,KAAM,GAAG,EAAGA,CAAS,EACvDH,EAASC,EAAOQ,EAAK,MAAON,CAAS,EAErC,IAAMO,EAAWC,EAAgBL,EAAMH,CAAS,EAC1CS,EAAWL,GAAU,eAAe,IAAIJ,CAAS,EACjDU,EAAKH,GAAYE,GAAY,KACnC,GAAI,CAACC,EAAI,SACT,IAAMC,EAASD,EAKf,GAJAb,EAASC,EAAOa,EAAO,aAAa,IAAI,EAAGX,CAAS,EACpDH,EAASC,EAAOa,EAAO,aAAa,MAAM,EAAGX,CAAS,EACtDH,EAASC,EAAOa,EAAO,aAAa,YAAY,EAAGX,CAAS,EAC5DH,EAASC,EAAOa,EAAO,aAAa,aAAa,EAAGX,CAAS,EACzDU,aAAc,kBAAoBA,aAAc,qBAAuBA,aAAc,kBACvF,QAAWE,KAAS,MAAM,KAAKF,EAAG,QAAU,CAAC,CAAC,EAC5Cb,EAASC,EAAOc,EAAM,aAAa,KAAK,EAAGZ,CAAS,CAG1D,CACA,OAAOF,CACT,CAEA,SAASe,GACPV,EACAW,EACAV,EACAW,EACgE,CAChE,IAAMC,EAAoC,CAAC,EACrCC,EAA0B,CAAC,EAC3BZ,EAAaD,GAAU,aAAa,YAAc,CAAC,EACnDc,EAAeH,EAAO,aAAa,sBAEzC,OAAW,CAACI,EAAKC,CAAK,IAAK,OAAO,QAAQN,CAAM,EAC1CK,KAAOd,IAAYW,EAASG,CAAG,EAAIC,GAEzC,GAAI,CAACF,EAAc,MAAO,CAAE,SAAAF,EAAU,SAAAC,CAAS,EAE/C,IAAMI,EAAanB,GAAgBC,EAAMC,CAAQ,EACjD,OAAW,CAACkB,EAAQF,CAAK,IAAK,OAAO,QAAQN,CAAM,EAAG,CACpD,GAAIQ,KAAUjB,EAAY,SAC1B,IAAMkB,EAAaF,EAAW,IAAI1B,GAAkB2B,CAAM,CAAC,EAC3D,GAAI,CAACC,GAAcA,EAAW,OAAS,EAAG,SAC1C,IAAMC,EAAS,MAAM,KAAKD,CAAU,EAAE,CAAC,EACnC,CAACC,GAAUA,KAAUR,IACzBA,EAASQ,CAAM,EAAIJ,EACnBH,EAAS,KAAK,CACZ,MAAOO,EACP,KAAM,iBACN,SAAUF,EACV,QAAS,aAAaA,CAAM,sBAAsBE,CAAM,GAC1D,CAAC,EACH,CACA,MAAO,CAAE,SAAAR,EAAU,SAAAC,CAAS,CAC9B,CAEA,SAASQ,GAA4BtB,EAAsC,CACzE,IAAMc,EAA0B,CAAC,EAC3BS,EAAW,MAAM,KAAKvB,EAAK,QAAQ,EAAE,OACxCO,GACCA,aAAc,kBAAoBA,aAAc,qBAAuBA,aAAc,iBACzF,EACA,QAAWiB,KAAWD,EAAU,CAE9B,GADI,CAACC,EAAQ,cACTA,EAAQ,cAAc,EAAG,SAC7B,IAAMC,EAAQD,EAAQ,MAAQA,EAAQ,IAAMA,EAAQ,aAAa,YAAY,GAAK,gBAClFV,EAAS,KAAK,CACZ,MAAAW,EACA,KAAM,iBACN,QAASD,EAAQ,mBAAqB,UAAUC,CAAK,qBACvD,CAAC,CACH,CACA,OAAOX,CACT,CAOA,SAASY,GAAqB1B,EAAgD,CAC5E,IAAM2B,EAAkC,CAAC,EACzC,GAAI,CACF,IAAMC,EAAO,IAAI,SAAS5B,CAAI,EAC9B,OAAW,CAACgB,EAAKa,CAAG,IAAKD,EAAK,QAAQ,EACpC,GAAID,EAAOX,CAAG,IAAM,OAAW,CAC7B,IAAMc,EAAWH,EAAOX,CAAG,EAC3BW,EAAOX,CAAG,EAAI,MAAM,QAAQc,CAAQ,EAAI,CAAC,GAAGA,EAAUD,CAAG,EAAI,CAACC,EAAUD,CAAG,CAC7E,MACEF,EAAOX,CAAG,EAAIa,CAGpB,MAAQ,CAER,CACA,OAAOF,CACT,CAOA,SAASI,GAAwB/B,EAA0C,CACzE,IAAMgC,EAA4B,CAAC,EACnC,QAAWR,KAAW,MAAM,KAAKxB,EAAK,QAAQ,EAAG,CAM/C,GAJE,EAAEwB,aAAmB,mBACrB,EAAEA,aAAmB,sBACrB,EAAEA,aAAmB,oBAEnB,CAACA,EAAQ,cAAgBA,EAAQ,cAAc,EAAG,SACtD,IAAMC,EAAQD,EAAQ,MAAQA,EAAQ,IAAMA,EAAQ,aAAa,YAAY,GAAK,gBAC5ES,EAAIT,EAAQ,SACZU,EAAaD,EAAE,aAAe,eAChCA,EAAE,aAAe,eACjBA,EAAE,gBAAkB,kBACpBA,EAAE,QAAU,UACZA,EAAE,SAAW,WACbA,EAAE,eAAiB,iBACnBA,EAAE,cAAgB,gBAClBA,EAAE,aAAe,eACjBA,EAAE,YAAc,cAChB,WACJD,EAAO,KAAK,CAAE,MAAAP,EAAO,WAAAS,EAAY,QAASV,EAAQ,mBAAqB,UAAUC,CAAK,qBAAsB,CAAC,CAC/G,CACA,OAAOO,CACT,CAUO,SAASG,GACdnC,EACAY,EACAwB,EACAnC,EAC+E,CAE/E,OAAIA,GAAU,eACZjB,EAAkB,IAAIgB,EAAMC,EAAS,aAAa,EAIpDoC,GAAwBrC,EAAMoC,CAAQ,EAE/B,MAAOzB,EAAiC2B,IAA6C,CAC1F,IAAMC,EAAqBD,EAC3B,GACE1B,EAAO,YACPX,GAAU,aAAa,kBAAoB,IAC3C,OAAOsC,GAAoB,wBAA2B,YAQlD,CANa,MAAMA,EAAmB,uBAAuB,SACxD,IAAI,QAAkBC,GAAY,CACvC,IAAMC,EAAK,OAAO,QAAQ,6CAA6CL,CAAQ,cAAc,EAC7FI,EAAQC,CAAE,CACZ,CAAC,CACF,EAEC,cAAO,cAAc,IAAI,YAAY,aAAc,CAAE,OAAQ,CAAE,SAAAL,CAAS,CAAE,CAAC,CAAC,EACrE,CAAE,QAAS,CAAC,CAAE,KAAM,OAAQ,KAAM,cAAcA,CAAQ,YAAa,CAAC,CAAE,EAInFlD,EAAoB,IAAIc,EAAM,CAAC,CAAC,EAChCf,EAAgB,OAAOe,CAAI,EAG3B,IAAM0C,EAAmBhB,GAAqB1B,CAAI,EAClDZ,EAAc,IAAIY,EAAM0C,CAAgB,EAExC,GAAM,CAAE,SAAUC,EAAgB,SAAUC,CAAc,EAAIlC,GAC5DV,EACAW,EACAV,EACAW,CACF,EACIgC,EAAc,OAAS,GACzB1D,EAAoB,IAAIc,EAAM,CAAC,GAAId,EAAoB,IAAIc,CAAI,GAAK,CAAC,EAAI,GAAG4C,CAAa,CAAC,EAI5F,IAAIC,EAAeF,EACnB,GAAI/B,EAAO,iBAAkB,CAC3B,IAAMkC,EAA2B,CAAC,EAClCD,EAAe,OAAO,YACpB,OAAO,QAAQF,CAAc,EAAE,OAAO,CAAC,CAAC3B,CAAG,IAAM,CAC/C,IAAM+B,EAAUL,EAAiB1B,CAAG,EAC9BgC,EAAWD,IAAY,QAAaA,IAAY,IAAMA,IAAY,KACxE,OAAIC,GACFF,EAAU,KAAK,CACb,MAAO9B,EACP,KAAM,aACN,QAAS,UAAUA,CAAG,uDACxB,CAAC,EAEI,CAACgC,CACV,CAAC,CACH,EACIF,EAAU,OAAS,GACrB5D,EAAoB,IAAIc,EAAM,CAAC,GAAId,EAAoB,IAAIc,CAAI,GAAK,CAAC,EAAI,GAAG8C,CAAS,CAAC,CAE1F,CAEAG,GAAejD,EAAM6C,CAAY,EAIjC,IAAMK,EAAaC,GAAmBlD,EAAU0C,CAAc,EAC9D,OAAIO,EAAW,OAAS,GAAGjE,EAAgB,IAAIe,EAAMkD,CAAU,EAG/D,OAAO,cAAc,IAAI,YAAY,gBAAiB,CAAE,OAAQ,CAAE,SAAAd,CAAS,CAAE,CAAC,CAAC,EAExE,IAAI,QAAuB,CAACI,EAASY,IAAW,CACrD,IAAMC,EAAYzC,EAAO,UAAU,UAC7B0C,EAAY,WAAW,IAAM,CAEjC,GAAI,CADYxE,EAAkB,IAAIkB,CAAI,EAC5B,OACdlB,EAAkB,OAAOkB,CAAI,EAC7B,IAAMuD,EACJ3C,EAAO,YACPZ,EAAK,aAAa,gBAAgB,GAClCA,EAAK,QAAQ,mBAAwB,OACnC,YAAc,uBACZwD,EAAoB,CACxB,MAAO,WACP,KAAM,UACN,QAASD,IAAkB,YACvB,kCAAkCF,CAAS,KAC3C,4CAA4CA,CAAS,KAC3D,EACMI,EAAuBrE,EAAc,IAAIY,CAAI,EAC7C0D,EAAoC,CACxC,OAAQH,EACR,cAAeI,GAAkB3D,EAAMjB,EAAW,IAAIiB,CAAI,EAAGhB,EAAkB,IAAIgB,CAAI,CAAC,EACxF,eAAgB,CAAC,EACjB,iBAAkBf,EAAgB,IAAIe,CAAI,GAAK,CAAC,EAChD,SAAU,CAAC,GAAId,EAAoB,IAAIc,CAAI,GAAK,CAAC,EAAIwD,CAAI,EACzD,GAAIC,IAAyB,QAAa,CAAE,gBAAiBA,CAAqB,CACpF,EACAxE,EAAgB,OAAOe,CAAI,EAC3Bd,EAAoB,OAAOc,CAAI,EAC/Bb,EAAmB,OAAOa,CAAI,EAC9BZ,EAAc,OAAOY,CAAI,EACzBwC,EAAQ,CACN,QAAS,CACP,CAAE,KAAM,OAAQ,KAAMgB,EAAK,OAAQ,EACnC,CAAE,KAAM,OAAQ,KAAM,KAAK,UAAUE,CAAU,CAAE,CACnD,CACF,CAAC,CACH,EAAGL,CAAS,EACZvE,EAAkB,IAAIkB,EAAM,CAAE,QAAAwC,EAAS,OAAAY,EAAQ,UAAAE,CAAU,CAAC,GAGxD1C,EAAO,YACPZ,EAAK,aAAa,gBAAgB,GAClCA,EAAK,QAAQ,mBAAwB,SAMrC4D,GAAiB5D,CAAI,EAAE,KAAK,SAAY,CACtC,GAAI,CAEFiD,GAAejD,EAAM2C,CAAc,EAGnC,QAASkB,EAAU,EAAGA,EAAU,GAChBC,GAAe9D,EAAM2C,EAAgB3D,EAAkB,IAAIgB,CAAI,CAAC,EACpE,SAAW,EAFY6D,IAGjCZ,GAAejD,EAAM2C,CAAc,EACnC,MAAMiB,GAAiB5D,EAAM,IAAK,GAAG,EAKvC,IAAI+D,EAA8B/D,EAClC,GAAI,CAACA,EAAK,YAAa,CAIrB,IAAMgE,EAHU,SAAS,cACvB,6EACF,GACuB,QAAQ,MAAM,EACrC,GAAIA,EAAO,CACTD,EAAaC,EACb,IAAMC,EAAUnF,EAAkB,IAAIkB,CAAI,EACpCkE,EAAcD,GAAS,UACzB,CAAE,QAAAzB,EAAS,OAAAY,EAAQ,UAAWa,EAAQ,SAAU,EAChD,CAAE,QAAAzB,EAAS,OAAAY,CAAO,EACtBtE,EAAkB,IAAIiF,EAAYG,CAAW,EAC7C7B,GAAwB0B,EAAY3B,CAAQ,CAC9C,CACF,CAQA,GALI2B,IAAe/D,GAAQf,EAAgB,IAAIe,CAAI,IACjDf,EAAgB,IAAI8E,EAAY9E,EAAgB,IAAIe,CAAI,CAAE,EAC1Df,EAAgB,OAAOe,CAAI,GAGzB,CAAC+D,EAAW,cAAc,EAAG,CAC/B,IAAME,EAAUnF,EAAkB,IAAIiF,CAAU,GAAKjF,EAAkB,IAAIkB,CAAI,EAC/E,GAAIiE,EAAS,CACPA,EAAQ,WAAW,aAAaA,EAAQ,SAAS,EACrDnF,EAAkB,OAAOiF,CAAU,EACnCjF,EAAkB,OAAOkB,CAAI,EAC7B,IAAMc,EAAW,CACf,GAAI5B,EAAoB,IAAI6E,CAAU,GAAK7E,EAAoB,IAAIc,CAAI,GAAK,CAAC,EAC7E,GAAGsB,GAA4ByC,CAAU,CAC3C,EACA7E,EAAoB,OAAO6E,CAAU,EACrC7E,EAAoB,OAAOc,CAAI,EAC/B,IAAMmE,EAAuB/E,EAAc,IAAIY,CAAI,EAC7C0D,EAAoC,CACxC,OAAQ,kBACR,cAAeC,GAAkBI,EAAYhF,EAAW,IAAIgF,CAAU,GAAKhF,EAAW,IAAIiB,CAAI,EAAGhB,EAAkB,IAAI+E,CAAU,GAAK/E,EAAkB,IAAIgB,CAAI,CAAC,EACjK,eAAgB,CAAC,EACjB,iBAAkBf,EAAgB,IAAI8E,CAAU,GAAK9E,EAAgB,IAAIe,CAAI,GAAK,CAAC,EACnF,SAAAc,EACA,kBAAmBiB,GAAwBgC,CAAU,EACrD,GAAII,IAAyB,QAAa,CAAE,gBAAiBA,CAAqB,CACpF,EACAlF,EAAgB,OAAO8E,CAAU,EACjC9E,EAAgB,OAAOe,CAAI,EAC3Bb,EAAmB,OAAO4E,CAAU,EACpC5E,EAAmB,OAAOa,CAAI,EAC9BZ,EAAc,OAAOY,CAAI,EACzBwC,EAAQ,CACN,QAAS,CACP,CAAE,KAAM,OAAQ,KAAM,+CAAgD,EACtE,CAAE,KAAM,OAAQ,KAAM,KAAK,UAAUkB,CAAU,CAAE,CACnD,CACF,CAAC,CACH,CACA,MACF,CAEAK,EAAW,cAAc,CAC3B,OAASK,EAAK,CACZhB,EAAOgB,aAAe,MAAQA,EAAM,IAAI,MAAM,OAAOA,CAAG,CAAC,CAAC,CAC5D,CACF,CAAC,CAIL,CAAC,CACH,CACF,CAEA,SAAS/B,GAAwBrC,EAAuBoC,EAAwB,CAEzEpC,EAA4C,sBAChDA,EAA4C,oBAAyB,GAEtEA,EAAK,iBAAiB,SAAWqE,GAAmB,CAClD,IAAMJ,EAAUnF,EAAkB,IAAIkB,CAAI,EAC1C,GAAI,CAACiE,EAAS,OAGd,GAAM,CAAE,QAAAzB,CAAQ,EAAIyB,EAChBA,EAAQ,WAAW,aAAaA,EAAQ,SAAS,EACrDnF,EAAkB,OAAOkB,CAAI,EAE7B,IAAMsE,EAAWX,GAAkB3D,EAAMjB,EAAW,IAAIiB,CAAI,EAAGhB,EAAkB,IAAIgB,CAAI,CAAC,EACpFuE,EAAenF,EAAc,IAAIY,CAAI,EAC3Cb,EAAmB,OAAOa,CAAI,EAC9BZ,EAAc,OAAOY,CAAI,EACzB,IAAMwE,EAAkBvF,EAAgB,IAAIe,CAAI,GAAK,CAAC,EACtDf,EAAgB,OAAOe,CAAI,EAC3B,IAAMyE,EAAevF,EAAoB,IAAIc,CAAI,GAAK,CAAC,EACvDd,EAAoB,OAAOc,CAAI,EAE/B,IAAM0E,EAAgBD,EACnB,OAAQE,GAAMA,EAAE,OAAS,YAAY,EACrC,IAAKA,GAAMA,EAAE,KAAK,EAEfjB,EAAoC,CACxC,OAAQc,EAAgB,OAAS,GAAKE,EAAc,OAAS,EAAI,UAAY,UAC7E,cAAeJ,EACf,eAAgBI,EAChB,iBAAkBF,EAClB,SAAU,CACR,GAAGA,EAAgB,IAAKI,IAAoB,CAC1C,MAAOA,EACP,KAAM,mBACN,QAAS,mBAAmBA,CAAC,oBAC/B,EAAE,EACF,GAAGH,CACL,EACA,GAAIF,IAAiB,QAAa,CAAE,gBAAiBA,CAAa,CACpE,EAEMM,EAAkB,CACtB,GAAIL,EAAgB,OAAS,CAAC,oCAAoCA,EAAgB,KAAK,IAAI,CAAC,EAAE,EAAI,CAAC,EACnG,GAAGC,EAAa,IAAKE,GAAMA,EAAE,OAAO,CACtC,EACMG,EAAcD,EAAgB,OAAS,UAAUA,EAAgB,KAAK,IAAI,CAAC,IAAM,GAEjFlD,EAAwB,CAC5B,QAAS,CACP,CAAE,KAAM,OAAQ,KAHP,2BAA2B,KAAK,UAAU2C,CAAQ,CAAC,GAAGQ,CAAW,EAGrD,EACrB,CAAE,KAAM,OAAQ,KAAM,KAAK,UAAUpB,CAAU,CAAE,CACnD,CACF,EAEIW,EAAE,cAAgB,OAAOA,EAAE,aAAgB,aAE7CA,EAAE,eAAe,EACjBA,EAAE,YAAY,QAAQ,QAAQ1C,CAAM,CAAC,GAEvCa,EAAQb,CAAM,CAChB,CAAC,EAGD3B,EAAK,iBAAiB,QAAS,IAAM,CACnCb,EAAmB,OAAOa,CAAI,EAC9BZ,EAAc,OAAOY,CAAI,EACzB,OAAO,cAAc,IAAI,YAAY,aAAc,CAAE,OAAQ,CAAE,SAAAoC,CAAS,CAAE,CAAC,CAAC,CAC9E,CAAC,EACH,CAMA,SAAS2C,EAAcxE,EAA4C0B,EAAiB,CAUlF,GATA1B,EAAG,MAAM,EAETA,EAAG,SAAS,EAOR,SAAS,YAAY,aAAc,GAAO0B,CAAC,GAAK1B,EAAG,QAAU0B,EAC/D,OAIF,IAAM+C,EAASzE,aAAc,oBAAsBjB,GAAuBD,GACtE2F,EACFA,EAAO,KAAKzE,EAAI0B,CAAC,EAEjB1B,EAAG,MAAQ0B,EAEb1B,EAAG,cAAc,IAAI,WAAW,QAAS,CAAE,QAAS,GAAM,WAAY,GAAM,UAAW,aAAc,KAAM0B,CAAE,CAAC,CAAC,EAC/G1B,EAAG,cAAc,IAAI,MAAM,SAAU,CAAE,QAAS,EAAK,CAAC,CAAC,CACzD,CAEA,SAAS0E,GAAgB1E,EAAsB2E,EAAwB,CACjE3F,EACFA,EAAe,KAAKgB,EAAI2E,CAAO,EAE/B3E,EAAG,QAAU2E,EAEf3E,EAAG,cAAc,IAAI,MAAM,SAAU,CAAE,QAAS,EAAK,CAAC,CAAC,CACzD,CAOA,SAAS4E,EACPC,EACAC,EACmE,CACnE,QAAWC,KAAQ,MAAM,KAAKF,EAAK,iBAAiB,GAAG,CAAC,EAAG,CACzD,IAAMG,EAAMD,EAAiB,WAC7B,GAAI,CAACC,EAAI,SACT,IAAMvB,EAAQuB,EAAG,cAA0EF,CAAQ,EACnG,GAAIrB,EAAO,OAAOA,EAClB,IAAMwB,EAASL,EAAkBI,EAAIF,CAAQ,EAC7C,GAAIG,EAAQ,OAAOA,CACrB,CACA,OAAO,IACT,CAEA,SAASC,GACPzF,EACA0F,EACAC,EACoB,CACpB,OAAO,MAAM,KAAK3F,EAAK,QAAQ,EAAE,OAC9BO,GACCA,aAAc,kBACdA,EAAG,OAASmF,GACZnF,EAAG,OAASoF,CAChB,CACF,CAGA,SAAStF,EACPL,EACAgB,EACmE,CAEnE,IAAM4E,EAAQ5F,EAAK,SAAS,UAAUgB,CAAG,EACzC,GACE,OAAO4E,GAAU,UACjBA,IAAU,OACTA,aAAiB,kBAAoBA,aAAiB,qBAAuBA,aAAiB,mBAE/F,OAAOA,EAET,GAAIA,aAAiB,cAAe,CAElC,IAAMC,EADQD,EAAM,CAAC,EAErB,GACEC,aAAoB,kBACpBA,aAAoB,qBACpBA,aAAoB,kBAEpB,OAAOA,CAEX,CAEA,IAAMC,EAAM,IAAI,OAAO9E,CAAG,EAEpB+E,EACJ/F,EAAK,cAA0E,UAAU8F,CAAG,IAAI,GAChG9F,EAAK,cACH,SAAS8F,CAAG,cAAcA,CAAG,YAAYA,CAAG,EAC9C,EACF,OAAIC,IAGFZ,EAAkB,SAAU,UAAUW,CAAG,IAAI,GAC7CX,EAAkB,SAAU,SAASW,CAAG,cAAcA,CAAG,YAAYA,CAAG,EAAE,EAE9E,CAEA,SAAS7C,GAAejD,EAAuBW,EAAuC,CACpF5B,EAAW,IAAIiB,EAAMW,CAAM,EAC3B,IAAMqF,EAAWhH,EAAkB,IAAIgB,CAAI,EACrCiG,EAAoC,CAAC,EAE3C,OAAW,CAACjF,EAAKC,CAAK,IAAK,OAAO,QAAQN,CAAM,EAAG,CACjD,IAAMuF,EAAQ7F,EAAgBL,EAAMgB,CAAG,EAEvC,GAAIkF,EAAO,CACLA,aAAiB,kBACnBC,GAAUD,EAAOlG,EAAMgB,EAAKC,CAAK,EAC7BiF,EAAM,OAAS,WACb,MAAM,QAAQjF,CAAK,EACrBgF,EAASjF,CAAG,EAAIyE,GAA0BzF,EAAM,WAAYgB,CAAG,EAC5D,OAAQoF,GAAMA,EAAE,OAAO,EACvB,IAAKA,GAAMA,EAAE,KAAK,EAErBH,EAASjF,CAAG,EAAIkF,EAAM,QAGxBD,EAASjF,CAAG,EAAIkF,EAAM,OAEfA,aAAiB,qBAC1BnB,EAAcmB,EAAO,OAAOjF,GAAS,EAAE,CAAC,EACxCgF,EAASjF,CAAG,EAAIkF,EAAM,OACbA,aAAiB,oBAC1BG,GAAkBH,EAAOjF,EAAOjB,EAAMgB,CAAG,EACzCiF,EAASjF,CAAG,EAAIkF,EAAM,SAClB,MAAM,KAAKA,EAAM,OAAO,EAAE,OAAQI,GAAMA,EAAE,QAAQ,EAAE,IAAKA,GAAMA,EAAE,KAAK,EACtEJ,EAAM,OAEZ,QACF,CAGA,IAAMK,EAASP,GAAU,IAAIhF,CAAG,EAChC,GAAIuF,EAAQ,CAIV,IAAIC,EAAuBD,EAC3B,GAAI,CAACA,EAAO,YAAa,CACvB,IAAME,EAAQF,EAAuB,GACrC,GAAIE,EAAM,CACR,IAAMC,EACJ,SAAS,eAAeD,CAAI,GAC5BtB,EAAkB,SAAU,IAAI,IAAI,OAAOsB,CAAI,CAAC,EAAE,EAChDC,IAAOF,EAAcE,EAC3B,CACF,CACIF,aAAuB,kBACzBL,GAAUK,EAAaxG,EAAMgB,EAAKC,CAAK,EACvCgF,EAASjF,CAAG,EAAIwF,EAAY,OAAS,WAAaA,EAAY,QAAUA,EAAY,OAC3EA,aAAuB,qBAChCzB,EAAcyB,EAAa,OAAOvF,GAAS,EAAE,CAAC,EAC9CgF,EAASjF,CAAG,EAAIwF,EAAY,OACnBA,aAAuB,mBAChCH,GAAkBG,EAAavF,EAAOjB,EAAMgB,CAAG,EAC/CiF,EAASjF,CAAG,EAAIwF,EAAY,SACxB,MAAM,KAAKA,EAAY,OAAO,EAAE,OAAQF,GAAMA,EAAE,QAAQ,EAAE,IAAKA,GAAMA,EAAE,KAAK,EAC5EE,EAAY,QAEhBG,GAAcH,EAAavF,CAAK,EAChCgF,EAASjF,CAAG,EAAIC,EAEpB,CACF,CAEA9B,EAAmB,IAAIa,EAAMiG,CAAQ,EAKpC,OAA8C,mBAC7C/G,EAAoB,IAAIc,CAAI,GAAK,CAAC,CACtC,CAEA,SAASmG,GACPD,EACAlG,EACAgB,EACAC,EACM,CACN,IAAMyE,EAAOQ,EAAM,KAAK,YAAY,EAEpC,GAAIR,IAAS,WAAY,CAEvB,GAAI,MAAM,QAAQzE,CAAK,EAAG,CACxB,IAAM2F,EAAWnB,GAA0BzF,EAAM,WAAYgB,CAAG,EAChE,QAAW6F,KAAOD,EAChB3B,GAAgB4B,EAAM5F,EAAoB,IAAI,MAAM,EAAE,SAAS4F,EAAI,KAAK,CAAC,EAE3E,MACF,CACA5B,GAAgBiB,EAAO,EAAQjF,CAAM,EACrC,MACF,CAEA,GAAIyE,IAAS,UAAYA,IAAS,QAAS,CACzC,IAAMjG,EAAM,OAAOwB,GAAS,EAAE,EACxB6F,EAAM,OAAOrH,CAAG,EACtB,GAAIA,IAAQ,IAAM,MAAMqH,CAAG,EAAG,CAC5B5H,EAAoB,IAAIc,CAAI,GAAG,KAAK,CAClC,MAAOgB,EACP,KAAM,gBACN,QAAS,IAAIA,CAAG,4BAA4B,KAAK,UAAUC,CAAK,CAAC,GACjE,SAAUA,CACZ,CAAC,EACD,MACF,CACA,IAAM8F,EAAMb,EAAM,MAAQ,GAAK,WAAWA,EAAM,GAAG,EAAI,KACjDc,EAAMd,EAAM,MAAQ,GAAK,WAAWA,EAAM,GAAG,EAAI,IACvD,GAAIY,EAAMC,GAAOD,EAAME,EAAK,CAC1B,IAAMC,EAAU,KAAK,IAAI,KAAK,IAAIH,EAAKC,CAAG,EAAGC,CAAG,EAChD9H,EAAoB,IAAIc,CAAI,GAAG,KAAK,CAClC,MAAOgB,EACP,KAAM,UACN,QAAS,IAAIA,CAAG,WAAW8F,CAAG,8BAA8BZ,EAAM,KAAO,GAAG,KAAKA,EAAM,KAAO,GAAG,iBAAiBe,CAAO,GACzH,SAAUH,EACV,OAAQG,CACV,CAAC,EACDf,EAAM,MAAQ,OAAOe,CAAO,CAC9B,MACEf,EAAM,MAAQ,OAAOY,CAAG,EAE1BZ,EAAM,cAAc,IAAI,WAAW,QAAS,CAAE,QAAS,GAAM,WAAY,GAAM,UAAW,aAAc,KAAM,OAAOY,CAAG,CAAE,CAAC,CAAC,EAC5HZ,EAAM,cAAc,IAAI,MAAM,SAAU,CAAE,QAAS,EAAK,CAAC,CAAC,EAC1D,MACF,CAEA,GAAIR,IAAS,QAAS,CACpB,IAAMwB,EAASzB,GAA0BzF,EAAM,QAASgB,CAAG,EAC3D,QAAWmG,KAASD,EAClB,GAAIC,EAAM,QAAU,OAAOlG,CAAK,EAAG,CAC7B1B,EACFA,EAAe,KAAK4H,EAAO,EAAI,EAE/BA,EAAM,QAAU,GAElBA,EAAM,cAAc,IAAI,MAAM,SAAU,CAAE,QAAS,EAAK,CAAC,CAAC,EAC1D,KACF,CAEF,MACF,CAEApC,EAAcmB,EAAO,OAAOjF,GAAS,EAAE,CAAC,CAC1C,CAQA,SAASoF,GACPe,EACAnG,EACAjB,EACAgB,EACM,CACN,GAAIoG,EAAO,SAAU,CACnB,IAAMC,EAAiB,MAAM,QAAQpG,CAAK,EACrCA,EAAoB,IAAI,MAAM,EAC/B,CAAC,OAAOA,GAAS,EAAE,CAAC,EACxB,QAAWqG,KAAO,MAAM,KAAKF,EAAO,OAAO,EACzCE,EAAI,SAAWD,EAAK,SAASC,EAAI,KAAK,EAExCF,EAAO,cAAc,IAAI,MAAM,SAAU,CAAE,QAAS,EAAK,CAAC,CAAC,EAC3D,MACF,CAEA,IAAMG,EAAS,OAAOtG,GAAS,EAAE,EAKjC,GAJAmG,EAAO,MAAQG,EAIXH,EAAO,QAAUG,EAAQ,CAC3B,IAAMC,EAAQD,EAAO,YAAY,EAC3BE,EAAU,MAAM,KAAKL,EAAO,OAAO,EAAE,KACxCd,GAAMA,EAAE,KAAK,KAAK,EAAE,YAAY,IAAMkB,GAASlB,EAAE,MAAM,KAAK,EAAE,YAAY,IAAMkB,CACnF,EACIC,EACFL,EAAO,MAAQK,EAAQ,MACdzH,GAAQgB,GAEjB9B,EAAoB,IAAIc,CAAI,GAAG,KAAK,CAClC,MAAOgB,EACP,KAAM,aACN,QAAS,IAAIA,CAAG,YAAYuG,CAAM,2CAClC,SAAUA,CACZ,CAAC,CAEL,CAEAH,EAAO,cAAc,IAAI,MAAM,SAAU,CAAE,QAAS,EAAK,CAAC,CAAC,CAC7D,CAEA,SAAST,GAAcpG,EAAaU,EAAsB,CACxD,IAAMyG,EAAOnH,EAAG,aAAa,MAAM,EAEnC,GAAImH,IAAS,YAAcA,IAAS,SAAU,CAC5CnH,EAAG,aAAa,eAAgB,OAAO,EAAQU,CAAM,CAAC,EACtDV,EAAG,cAAc,IAAI,WAAW,QAAS,CAAE,QAAS,EAAK,CAAC,CAAC,EAC3D,MACF,CAEA,GAAImH,IAAS,QAAS,CACpBnH,EAAG,aAAa,eAAgB,MAAM,EACtCA,EAAG,cAAc,IAAI,WAAW,QAAS,CAAE,QAAS,EAAK,CAAC,CAAC,EAC3D,MACF,CAEA,GAAImH,IAAS,aAAc,CAEzB,IAAMR,EAAS,MAAM,KAAK3G,EAAG,iBAAiB,gBAAgB,CAAC,EAC/D,QAAW4G,KAASD,EAElB,IADaC,EAAM,aAAa,YAAY,GAAKA,EAAM,aAAa,YAAY,GAAKA,EAAM,aAAe,IAAI,KAAK,IACvG,OAAOlG,CAAK,EAAG,CACzBkG,EAAM,aAAa,eAAgB,MAAM,EACzCA,EAAM,cAAc,IAAI,WAAW,QAAS,CAAE,QAAS,EAAK,CAAC,CAAC,EAC9D,QAAWQ,KAAST,EACdS,IAAUR,GAAOQ,EAAM,aAAa,eAAgB,OAAO,EAEjE,KACF,CAEF,MACF,CAGA,IAAMnH,EAASD,EAOf,GANA,QAAQ,IAAI,8BAA+B,CACzC,IAAKA,EAAG,QAAS,KAAAmH,EAAM,kBAAmBlH,EAAO,kBACjD,GAAID,EAAG,GAAI,UAAWA,EAAG,aAAa,YAAY,EAClD,mBAAoBC,EAAO,aAAe,IAAI,MAAM,EAAG,EAAE,CAC3D,CAAC,EAEGA,EAAO,kBAAmB,CAC5BA,EAAO,MAAM,EAIb,IAAMoH,EAAQ,SAAS,YAAY,EACnCA,EAAM,mBAAmBpH,CAAM,EAC/B,IAAMqH,EAAM,OAAO,aAAa,EAChCA,GAAK,gBAAgB,EACrBA,GAAK,SAASD,CAAK,EAEnB,IAAME,EAAO,OAAO7G,GAAS,EAAE,EAC/B,QAAQ,IAAI,+CAAgD,KAAK,UAAU6G,CAAI,CAAC,EAShF,IAAIC,EAAW,GACf,GAAI,CACF,IAAMC,EAAK,IAAI,aACfA,EAAG,QAAQ,aAAcF,CAAI,EAC7BtH,EAAO,cAAc,IAAI,eAAe,QAAS,CAC/C,QAAS,GAAM,WAAY,GAAM,SAAU,GAAM,cAAewH,CAClE,CAAC,CAAC,EACFD,GAAYvH,EAAO,aAAe,IAAI,KAAK,EAAE,OAAS,EACtD,QAAQ,IAAI,gDAAiDuH,EAAU,KAAK,WAAWvH,EAAO,aAAe,IAAI,MAAM,EAAG,EAAE,CAAC,CAAC,CAChI,OAAS6D,EAAG,CACV,QAAQ,IAAI,+CAAgDA,CAAC,CAC/D,CAEA,GAAI,CAAC0D,EAAU,CAIb,IAAMtF,EAAK,SAAS,YAAY,aAAc,GAAOqF,CAAI,EACzDC,GAAYvH,EAAO,aAAe,IAAI,KAAK,EAAE,OAAS,EACtD,QAAQ,IAAI,sDAAuDiC,EAAI,YAAasF,EAAU,KAAK,WAAWvH,EAAO,aAAe,IAAI,MAAM,EAAG,EAAE,CAAC,CAAC,CACvJ,CAEA,GAAI,CAACuH,EAIH,GAAI,CACFvH,EAAO,cAAc,IAAI,WAAW,cAAe,CACjD,QAAS,GAAM,WAAY,GAAM,SAAU,GAC3C,UAAW,aAAc,KAAMsH,CACjC,CAAC,CAAC,EACFC,GAAYvH,EAAO,aAAe,IAAI,KAAK,EAAE,OAAS,EACtD,QAAQ,IAAI,sDAAuDuH,EAAU,KAAK,WAAWvH,EAAO,aAAe,IAAI,MAAM,EAAG,EAAE,CAAC,CAAC,CACtI,OAAS6D,EAAG,CACV,QAAQ,IAAI,qDAAsDA,CAAC,CACrE,CAGF,GAAI,CAAC0D,EAAU,CAIbvH,EAAO,YAAcsH,EACrB,IAAMG,EAAK,SAAS,YAAY,EAChCA,EAAG,mBAAmBzH,CAAM,EAC5ByH,EAAG,SAAS,EAAK,EACjBJ,GAAK,gBAAgB,EACrBA,GAAK,SAASI,CAAE,EAChB,QAAQ,IAAI,4EAA6E,KAAK,WAAWzH,EAAO,aAAe,IAAI,MAAM,EAAG,EAAE,CAAC,CAAC,CAClJ,CAGAA,EAAO,cAAc,IAAI,WAAW,QAAS,CAC3C,QAAS,GAAM,WAAY,GAAM,UAAW,aAAc,KAAMsH,CAClE,CAAC,CAAC,EACF,QAAQ,IAAI,wDAAyD,KAAK,WAAWtH,EAAO,aAAe,IAAI,MAAM,EAAG,EAAE,CAAC,CAAC,CAC9H,MACE,QAAQ,IAAI,iFAAiF,EAC7FD,EAAG,cAAc,IAAI,MAAM,QAAS,CAAE,QAAS,EAAK,CAAC,CAAC,EACtDA,EAAG,cAAc,IAAI,MAAM,SAAU,CAAE,QAAS,EAAK,CAAC,CAAC,CAE3D,CAMA,SAASoD,GACP3D,EACAW,EACAqF,EACyB,CACzB,IAAMrE,EAAkC,CAAC,EACnCC,EAAO,IAAI,SAAS5B,CAAI,EACxBiG,EAAW9G,EAAmB,IAAIa,CAAI,EAE5C,OAAW,CAACgB,EAAKa,CAAG,IAAKD,EAAK,QAAQ,EACpC,GAAID,EAAOX,CAAG,IAAM,OAAW,CAE7B,IAAMc,EAAWH,EAAOX,CAAG,EACvB,MAAM,QAAQc,CAAQ,EACxBA,EAAS,KAAKD,CAAG,EAEjBF,EAAOX,CAAG,EAAI,CAACc,EAAUD,CAAG,CAEhC,MACEF,EAAOX,CAAG,EAAIa,EAOlB,GAAIlB,EACF,QAAWK,KAAO,OAAO,KAAKL,CAAM,EAAG,CACrC,GAAIK,KAAOW,EAAQ,SAEnB,GAAIsE,GAAYjF,KAAOiF,EAAU,CAC/BtE,EAAOX,CAAG,EAAIiF,EAASjF,CAAG,EAC1B,QACF,CAEA,IAAMT,EACJF,EAAgBL,EAAMgB,CAAG,GACzBgF,GAAU,IAAIhF,CAAG,GACjB,KACF,GAAKT,EACL,GAAIA,aAAc,kBAAoBA,EAAG,OAAS,WAChDoB,EAAOX,CAAG,EAAIT,EAAG,gBACRA,aAAc,kBAAoBA,aAAc,qBAAuBA,aAAc,kBAC9FoB,EAAOX,CAAG,EAAIT,EAAG,UACZ,CAEL,IAAMmH,EAAOnH,EAAG,aAAa,MAAM,EAC/BmH,IAAS,YAAcA,IAAS,SAClC/F,EAAOX,CAAG,EAAIT,EAAG,aAAa,cAAc,IAAM,OAElDoB,EAAOX,CAAG,EAAKT,EAAmB,aAAa,KAAK,GAAK,EAE7D,CACF,CAGF,OAAOoB,CACT,CAaA,SAASuG,GAAoBjH,EAAeV,EAAyB,CACnE,IAAM4H,EAAWlH,EAAM,MAAM,2BAA2B,EAExD,GADI,CAACkH,GACD5H,aAAc,kBAAoBA,EAAG,OAAS,OAAQ,OAAOU,EACjE,IAAMmH,GAAc7H,EAAwB,MAAQA,EAAG,IAAM,IAAI,YAAY,EAC7E,GAAI,CAAC,OAAO,KAAK6H,CAAS,EAAG,OAAOnH,EACpC,GAAM,CAAC,CAAEoH,EAAMC,EAAOC,CAAG,EAAIJ,EAC7B,MAAO,GAAGG,CAAK,IAAIC,CAAG,IAAIF,CAAI,EAChC,CAEO,SAASG,GACdjI,EACAU,EACM,CACN,GAAIV,aAAc,iBAAkB,CAClC,IAAMmF,EAAOnF,EAAG,KAAK,YAAY,EAC7BmF,IAAS,WACXT,GAAgB1E,EAAI,EAAQU,CAAM,EACzByE,IAAS,QACdnF,EAAG,QAAU,OAAOU,CAAK,IACvB1B,EAAgBA,EAAe,KAAKgB,EAAI,EAAI,EAC3CA,EAAG,QAAU,GAClBA,EAAG,cAAc,IAAI,MAAM,SAAU,CAAE,QAAS,EAAK,CAAC,CAAC,GAGzDwE,EAAcxE,EAAI2H,GAAoB,OAAOjH,GAAS,EAAE,EAAGV,CAAE,CAAC,CAElE,MAAWA,aAAc,oBACvBwE,EAAcxE,EAAI,OAAOU,GAAS,EAAE,CAAC,EAC5BV,aAAc,kBACvB8F,GAAkB9F,EAAIU,CAAK,EAE3B0F,GAAcpG,EAAIU,CAAK,CAE3B,CAWA,SAAS2C,GACP5D,EACAyI,EAAQ,IACRC,EAAa,IACE,CACf,OAAO,IAAI,QAAelG,GAAY,CACpC,IAAImG,EAAU,GACVC,EAAsD,KAEpDC,EAAS,IAAY,CACrBF,IACJA,EAAU,GACVG,EAAS,WAAW,EAChBF,IAAkB,MAAM,aAAaA,CAAa,EACtDpG,EAAQ,EACV,EAEMsG,EAAW,IAAI,iBAAiB,IAAM,CACtCF,IAAkB,MAAM,aAAaA,CAAa,EACtDA,EAAgB,WAAWC,EAAQH,CAAU,CAC/C,CAAC,EAEDI,EAAS,QAAQ9I,EAAM,CAAE,UAAW,GAAM,QAAS,GAAM,WAAY,GAAM,cAAe,EAAK,CAAC,EAEhG,WAAW6I,EAAQJ,CAAK,EACxBG,EAAgB,WAAWC,EAAQH,CAAU,CAC/C,CAAC,CACH,CAMA,SAAS5E,GACP9D,EACAW,EACAqF,EACU,CACV,IAAM+C,EAAkB,CAAC,EACzB,OAAW,CAAC/H,EAAKgI,CAAQ,IAAK,OAAO,QAAQrI,CAAM,EAAG,CACpD,IAAMJ,EAAKF,EAAgBL,EAAMgB,CAAG,GAAMgF,GAAU,IAAIhF,CAAG,GAAK,KAC3DT,IACDA,aAAc,kBAAoBA,EAAG,OAAS,WAC5CA,EAAG,UAAY,EAAQyI,GAAWD,EAAM,KAAK/H,CAAG,GAEpDT,aAAc,kBACdA,aAAc,qBACdA,aAAc,oBAEVA,EAAG,QAAU,OAAOyI,GAAY,EAAE,GAAGD,EAAM,KAAK/H,CAAG,EAE3D,CACA,OAAO+H,CACT,CAOA,SAAS5F,GACPlD,EACAU,EACU,CACV,OAAKV,GAAU,aAAa,UAAU,OAC/BA,EAAS,YAAY,SAAS,OAAQgJ,GAAa,EAAEA,KAAYtI,EAAO,EAD1B,CAAC,CAExD,CAWA,eAAsBuI,GAAmB3I,EAAaU,EAA+B,CACnF,IAAM6G,EAAO,OAAO7G,GAAS,EAAE,EAAE,KAAK,EACtC,QAAQ,IAAI,4DAA6D,KAAK,UAAU6G,CAAI,CAAC,EAC7FvH,EAAG,cAAc,IAAI,WAAW,QAAS,CAAE,QAAS,GAAM,WAAY,EAAK,CAAC,CAAC,EAG7E,IAAM4I,EAAU,MAAM,IAAI,QAAyB3G,GAAY,CAC7D,IAAM4G,EAAW,KAAK,IAAI,EAAI,IACxBC,EAAO,IAAY,CAIvB,IAAMC,EACJ,SAAS,cAAc,kBAAkB,GACzC,SAAS,cAAc,iBAAiB,GAAG,QAAQ,kBAAkB,GACrE,KACF,GAAIA,EAAW,CACb9G,EAAQ8G,CAAS,EACjB,MACF,CACA,GAAI,KAAK,IAAI,GAAKF,EAAU,CAAE5G,EAAQ,IAAI,EAAG,MAAQ,CACrD,WAAW6G,EAAM,EAAE,CACrB,EACAA,EAAK,CACP,CAAC,EAED,GAAI,CAACF,EAAS,CACZ,QAAQ,KAAK,mEAAmE,EAChF,MACF,CAEA,IAAMI,EAAU,MAAM,KAAKJ,EAAQ,iBAAiB,iBAAiB,CAAC,EACtE,QAAQ,IAAI,gDAAiDI,EAAQ,OAAQ,SAAS,EAEtF,IAAMC,EAAa1B,EAAK,YAAY,EAC9B2B,EAAQF,EAAQ,KAAMjC,GAAQ,CAClC,IAAMoC,GAAapC,EAAI,aAAa,YAAY,GAAK,IAAI,YAAY,EAC/DqC,GAAarC,EAAI,aAAa,YAAY,GAAK,IAAI,YAAY,EAC/DsC,GAAWtC,EAAI,aAAe,IAAI,KAAK,EAAE,YAAY,EAC3D,OAAOoC,IAAcF,GAAcG,IAAcH,GAAcI,IAAYJ,CAC7E,CAAC,EAEGC,GACF,QAAQ,IAAI,oDAAqDA,EAAM,aAAa,KAAK,CAAC,EAC1FA,EAAM,cAAc,IAAI,WAAW,QAAS,CAAE,QAAS,GAAM,WAAY,EAAK,CAAC,CAAC,GAEhF,QAAQ,KAAK,sDAAuD,KAAK,UAAU3B,CAAI,EACrF,aAAcyB,EAAQ,IAAKjD,GAAMA,EAAE,aAAa,KAAK,CAAC,CAAC,CAE7D,CCvsCA,SAASuD,GAAKC,EAA+CC,EAAuBC,EAAwB,CAC1G,OAAO,cACL,IAAI,YAAYF,EAAM,CAAE,OAAQ,CAAE,KAAAC,EAAM,SAAAC,CAAS,CAAE,CAAC,CACtD,CACF,CAOA,SAASC,GAAWF,EAAuBG,EAAiC,CAE1E,GAAIH,EAAK,QAAQ,WAAgB,OAAW,MAAO,GAEnD,QAAWI,KAAYD,EAAO,QAC5B,GAAI,CACF,GAAIH,EAAK,QAAQI,CAAQ,EAAG,MAAO,EACrC,MAAQ,CAER,CAEF,MAAO,EACT,CAEA,SAASC,GAAkBC,EAAkBC,EAAmB,CAC9D,IAAMC,EAAS,IAAID,CAAC,GACpB,MAAO,GAAGD,EAAS,MAAM,EAAG,KAAK,IAAI,EAAG,GAAKE,EAAO,MAAM,CAAC,CAAC,GAAGA,CAAM,EACvE,CAEA,SAASC,GAAiBC,EAA4C,CACpE,IAAMC,EAAQ,IAAI,IAAYC,CAAyB,EACvD,OAAW,CAAE,KAAAZ,EAAM,KAAAa,CAAK,IAAKC,EAAsB,EAC7CJ,GAAeV,IAASU,GAC5BC,EAAM,IAAIE,CAAI,EAEhB,OAAOF,CACT,CAEA,SAASI,GAAqBT,EAAkBI,EAAuC,CACrF,IAAMM,EAAOP,GAAiBC,CAAW,EACzC,GAAI,CAACM,EAAK,IAAIV,CAAQ,EAAG,OAAOA,EAChC,IAAIW,EAAI,EACJC,EAAYb,GAAkBC,EAAUW,CAAC,EAC7C,KAAOD,EAAK,IAAIE,CAAS,GACvBD,IACAC,EAAYb,GAAkBC,EAAUW,CAAC,EAE3C,OAAOC,CACT,CAEA,SAASC,GAAyBnB,EAAgC,CAChE,MAAO,EAAAA,EAAK,aAAa,UAAU,GAAG,KAAK,EAAE,MAC/C,CAEA,eAAeoB,EAAapB,EAAuBG,EAAuC,CACxF,GAAID,GAAWF,EAAMG,CAAM,EAAG,OAC9B,IAAMkB,EAAeC,GAAsBtB,CAAI,EAE/C,GAAImB,GAAyBnB,CAAI,GAAKG,EAAO,kBAAoB,QAAS,CAIxE,GAHIkB,GACF,MAAME,EAAmBvB,CAAI,EAE3BG,EAAO,MAAO,CAChB,IAAMqB,EAAOrB,EAAO,gBACpB,QAAQ,IAAI,oFAAoFqB,CAAI,GAAG,CACzG,CACA,MACF,CAGA,IAAIC,EACJ,OAAW,CAACrB,EAAUsB,CAAG,IAAK,OAAO,QAAQvB,EAAO,SAAS,EAC3D,GAAI,CACF,GAAIH,EAAK,QAAQI,CAAQ,EAAG,CAC1BqB,EAAWC,EACX,KACF,CACF,MAAQ,CAER,CAGF,IAAMC,EAAWC,GAAY5B,EAAMyB,CAAQ,EACrCI,EAAed,GAAqBY,EAAS,KAAM3B,CAAI,EACzD6B,IAAiBF,EAAS,MAAQxB,EAAO,OAC3C,QAAQ,KAAK,uCAAuCwB,EAAS,IAAI,iBAAiBE,CAAY,GAAG,EAEnGF,EAAS,KAAOE,EAEZ1B,EAAO,OACT2B,GAAgBH,EAAS,KAAMA,EAAS,WAAW,EAGrD,IAAMI,EAAUC,GAAoBhC,EAAMG,EAAQwB,EAAS,KAAMA,CAAQ,EAEzE,MAAMM,GAAiBjC,EAAM2B,EAAUI,CAAO,EAC9CG,EAAgB,IAAIlC,CAAI,EACxBmC,KAIA,IAAMC,EAAgBpC,EAAK,cACzB,qEACF,GAAK,KACCqC,EAAgB,OAA8C,sBAA2B,CAAC,EAC5FhB,GAAgBA,IAAiBM,EAAS,MAC5C,OAAOU,EAAYhB,CAAY,EAEjCgB,EAAYV,EAAS,IAAI,EAAIS,EAEzBjC,EAAO,OACT,QAAQ,IAAI,6BAA6BwB,EAAS,IAAI,GAAIA,CAAQ,EAGpE7B,GAAK,kBAAmBE,EAAM2B,EAAS,IAAI,CAC7C,CAEA,eAAeW,GAAetC,EAAuBG,EAAuC,CAC1F,IAAMU,EAAOS,GAAsBtB,CAAI,EACvC,GAAI,CAACa,EAAM,OAEX,MAAMU,EAAmBvB,CAAI,EAC7BkC,EAAgB,OAAOlC,CAAI,EAC3B,IAAMqC,EAAe,OAA8C,oBAC/DA,GAAa,OAAOA,EAAYxB,CAAI,EAEpCV,EAAO,OACT,QAAQ,IAAI,+BAA+BU,CAAI,EAAE,EAGnDf,GAAK,oBAAqBE,EAAMa,CAAI,CACtC,CAMA,IAAI0B,EAAoC,KAGlCL,EAAkB,IAAI,QAGxBC,GAAsB,EAGpBK,GAAmB,IAAI,IACvBC,GAA0B,IAO5BC,EAA0D,KACxDC,GAA4B,IAO9BC,EAAiE,KAC/DC,GAA2B,IAI3BjC,EAA4B,IAAI,IAEtC,SAASkC,GAAqB3C,EAA8B,CACtDuC,GAAmB,aAAaA,CAAiB,EACrDA,EAAoB,WAAW,IAAM,CACnCA,EAAoB,KACfK,GAAiB5C,CAAM,CAC9B,EAAGwC,EAAyB,CAC9B,CAOA,SAASK,GAA4B7C,EAA8B,CAC7DyC,GAA0B,aAAaA,CAAwB,EACnEA,EAA2B,WAAW,IAAM,CAC1CA,EAA2B,KACtBG,GAAiB5C,CAAM,CAC9B,EAAG0C,EAAwB,CAC7B,CAGA,SAASI,GAAkBC,EAAwB,CACjD,IAAMC,EAAMD,EAAK,QAAQ,YAAY,EAKrC,GAJIC,IAAQ,SAAWA,IAAQ,YAAcA,IAAQ,UAIjDA,EAAI,SAAS,GAAG,EAAG,MAAO,GAC9B,IAAMC,EAAOF,EAAK,aAAa,MAAM,EAErC,GADIE,GAASC,EAAyC,SAASD,CAAI,GAC/DF,EAAK,cAAc,yBAAyB,EAAG,MAAO,GAC1D,QAAWI,KAAKD,EACd,GAAIH,EAAK,cAAc,UAAUI,CAAC,IAAI,EAAG,MAAO,GAElD,MAAO,EACT,CAEA,SAASC,GAAmBvD,EAAuBG,EAA8B,CAC/E,IAAMqD,EAAWhB,GAAiB,IAAIxC,CAAI,EACtCwD,GAAU,aAAaA,CAAQ,EACnChB,GAAiB,IACfxC,EACA,WAAW,IAAM,CACfwC,GAAiB,OAAOxC,CAAI,EACvBoB,EAAapB,EAAMG,CAAM,CAChC,EAAGsC,EAAuB,CAC5B,CACF,CAEA,SAASgB,GAA2BC,EAAgBvD,EAA8B,CAChF,IAAMwD,EAAQ,SAAS,eAAeD,CAAM,EACxCC,aAAiB,iBAAmBzB,EAAgB,IAAIyB,CAAK,GAC/DJ,GAAmBI,EAAOxD,CAAM,CAEpC,CAEA,SAASyD,GAAiBC,EAAqC,CAC7D,IAAMC,EAAUD,EAAG,QAAQ,MAAM,EACjC,GAAIC,aAAmB,gBAAiB,OAAOA,EAC/C,IAAMC,EAAiBF,EAAsF,KAC7G,GAAIE,aAAyB,gBAAiB,OAAOA,EACrD,IAAML,EAASG,EAAG,aAAa,MAAM,EACrC,GAAIH,EAAQ,CACV,IAAMM,EAAO,SAAS,eAAeN,CAAM,EAC3C,GAAIM,aAAgB,gBAAiB,OAAOA,CAC9C,CACA,OAAO,IACT,CAEA,SAASC,GAAc9D,EAA8B,CAC/CoC,IAEJA,EAAW,IAAI,iBAAkB2B,GAAc,CAC7C,QAAWC,KAAYD,EAAW,CAChC,GAAIC,EAAS,OAAS,cAAgBA,EAAS,kBAAkB,QAAS,CACxE,IAAMC,EAASD,EAAS,OAClBE,EAAYT,GAAiBQ,CAAM,EACrCC,GAAanC,EAAgB,IAAImC,CAAS,EAC5Cd,GAAmBc,EAAWlE,CAAM,EAC3BiE,aAAkB,gBACtBhD,EAAagD,EAAQjE,CAAM,EACvB8C,GAAkBmB,CAAM,GAAK,CAACA,EAAO,QAAQ,MAAM,GAC5DtB,GAAqB3C,CAAM,EAGzBgE,EAAS,gBAAkB,QAAUA,EAAS,UAChDV,GAA2BU,EAAS,SAAUhE,CAAM,EAEtD,QACF,CAEA,QAAW+C,KAAQiB,EAAS,WAAY,CACtC,GAAI,EAAEjB,aAAgB,SAAU,SAEhC,GAAIA,aAAgB,gBAAiB,CAC9B9B,EAAa8B,EAAM/C,CAAM,EAC9B,QACF,CAGA,IAAMmE,EAAapB,EAAK,QAAQ,MAAM,EAClCoB,aAAsB,iBAAmBpC,EAAgB,IAAIoC,CAAU,GAAKrB,GAAkBC,CAAI,GACpGK,GAAmBe,EAAYnE,CAAM,EAIvC,QAAWH,KAAQ,MAAM,KAAKkD,EAAK,iBAAkC,MAAM,CAAC,EACrE9B,EAAapB,EAAMG,CAAM,EAM5B8C,GAAkBC,CAAI,GAAK,CAACA,EAAK,QAAQ,MAAM,IACjDJ,GAAqB3C,CAAM,EAIvB+C,EAAK,QAAQ,YAAY,EAAE,SAAS,GAAG,GACzCF,GAA4B7C,CAAM,EAGxC,CAEA,QAAW+C,KAAQiB,EAAS,aAAc,CACxC,GAAI,EAAEjB,aAAgB,SAAU,SAEhC,IAAMqB,EAAQrB,aAAgB,gBAC1B,CAACA,CAAI,EACL,MAAM,KAAKA,EAAK,iBAAkC,MAAM,CAAC,EAE7D,QAAWlD,KAAQuE,EACZjC,GAAetC,EAAMG,CAAM,CAEpC,CACF,CACF,CAAC,EAEDoC,EAAS,QAAQ,SAAS,KAAM,CAC9B,UAAW,GACX,QAAS,GACT,WAAY,GACZ,kBAAmB,EACrB,CAAC,EACH,CAMA,SAASiC,GAAsBrE,EAA8B,CAE3D,OAAO,iBAAiB,aAAc,IAAMsE,EAAUtE,CAAM,CAAC,EAG7D,IAAMuE,EAAW,CACf,UAAW,QAAQ,UAAU,KAAK,OAAO,EACzC,aAAc,QAAQ,aAAa,KAAK,OAAO,CACjD,EAEA,QAAQ,UAAY,YAAaC,EAAM,CACrCD,EAAS,UAAU,GAAGC,CAAI,EAC1BF,EAAUtE,CAAM,CAClB,EAEA,QAAQ,aAAe,YAAawE,EAAM,CACxCD,EAAS,aAAa,GAAGC,CAAI,EAC7BF,EAAUtE,CAAM,CAClB,EAEA,OAAO,iBAAiB,WAAY,IAAMsE,EAAUtE,CAAM,CAAC,CAC7D,CAMA,eAAesE,EAAUtE,EAAuC,CAC9D,IAAMoE,EAAQ,MAAM,KAAK,SAAS,iBAAkC,MAAM,CAAC,EAC3E,MAAM,QAAQ,WAAWA,EAAM,IAAKvE,GAASoB,EAAapB,EAAMG,CAAM,CAAC,CAAC,CAC1E,CAOA,IAAMyE,GAAwB,IAAI,IAAI,CACpC,WAAY,SAAU,OAAQ,SAAU,QAAS,SAAU,OAC7D,CAAC,EAWD,SAASC,GACPC,EACAC,EACAC,EAAU,IAAI,IACmC,CACjD,GAAIA,EAAQ,IAAIF,CAAI,EAAG,MAAO,CAAC,EAC/BE,EAAQ,IAAIF,CAAI,EAEhB,IAAMG,EAA2D,CAAC,EAElE,QAAWpB,KAAM,MAAM,KAAKiB,EAAK,iBAAiB,GAAG,CAAC,EAAG,CACvD,IAAMI,EAAKrB,EAAG,WACd,GAAI,CAACqB,EAAI,SAIT,IAAMC,EAAOJ,GAAalB,EAC1BoB,EAAQ,KAAK,GAAGJ,GAA0BK,EAAIC,EAAMH,CAAO,CAAC,CAC9D,CAGA,GAAIF,aAAgB,WAAY,CAC9B,IAAM1E,EACJ,2IAIF,QAAWyD,KAAM,MAAM,KAAKiB,EAAK,iBAA8B1E,CAAQ,CAAC,EAAG,CAGzE,GAFIyD,aAAc,kBAAoBe,GAAsB,IAAIf,EAAG,KAAK,YAAY,CAAC,GAEjFA,EAAG,QAAQ,MAAM,EAAG,SACxB,IAAMuB,EAAOvB,EAAG,sBAAsB,EAClCuB,EAAK,QAAU,GAAKA,EAAK,SAAW,GACpCL,GACFE,EAAQ,KAAK,CAAE,GAAApB,EAAI,WAAYkB,CAAU,CAAC,CAE9C,CACF,CAEA,OAAOE,CACT,CAUA,eAAelC,GAAiB5C,EAAuC,CACrE,GAAI,CAACkF,EAAkB,EAAG,OAK1B,IAAMC,EAAsB,kFAGtBC,EAA+B,kDAC/BC,EAAiB,6EAKjBC,EAAgB,MAAM,KAC1B,SAAS,iBACP,8WAOF,CACF,EAAsF,OAAQ5B,GAAO,CACnG,GAAIA,aAAc,kBAAoBe,GAAsB,IAAIf,EAAG,KAAK,YAAY,CAAC,EACnF,eAAQ,IAAI,iDAAiDA,EAAG,IAAI,YAAYA,EAAG,IAAI,SAASA,EAAG,EAAE,IAAI,EAClG,GAET,IAAMuB,EAAOvB,EAAG,sBAAsB,EACtC,OAAIuB,EAAK,QAAU,GAAKA,EAAK,SAAW,GACtC,QAAQ,IAAI,yDAA0DvB,EAAuC,IAAI,SAASA,EAAG,EAAE,IAAI,EAC5H,IAEF,EACT,CAAC,EAGK6B,EAAgBb,GAA0B,SAAS,KAAM,IAAI,EAGnE,GAFA,QAAQ,IAAI,+BAA+BY,EAAa,MAAM,gBAAgBC,EAAc,MAAM,2BAA2B,EAEzHD,EAAa,SAAW,GAAKC,EAAc,SAAW,EAAG,OAI7D,IAAMC,EAAS,IAAI,IAEnB,QAAWC,KAASH,EAAc,CAChC,IAAII,EAA4BD,EAAM,cAClCE,EAA0BF,EAAM,eAAiB,SAAS,KAE9D,KAAOC,GAAaA,IAAc,SAAS,MAAM,CAM/C,GAJEA,EAAU,cAAcN,CAA4B,IAAM,MAC1D,MAAM,KAAKM,EAAU,iBAAiB,QAAQ,CAAC,EAAE,KAC9CE,GAAMP,EAAe,KAAKO,EAAE,aAAe,EAAE,CAChD,EACgB,CAChBD,EAAiBD,EACjB,KACF,CACAA,EAAYA,EAAU,aACxB,CAEA,QAAQ,IAAI,sCAAuCD,EAA0C,IAAI,SAASA,EAAM,EAAE,4BAA6BE,CAAc,EACxJH,EAAO,IAAIG,CAAc,GAAGH,EAAO,IAAIG,EAAgB,CAAC,CAAC,EAC9DH,EAAO,IAAIG,CAAc,EAAG,KAAKF,CAAK,CACxC,CAGA,OAAW,CAAE,GAAA/B,EAAI,WAAAmC,CAAW,IAAKN,EAAe,CAC9C,IAAIG,EAA4BG,EAAW,cACvCF,EAA0BE,EAAW,eAAiB,SAAS,KAEnE,KAAOH,GAAaA,IAAc,SAAS,MAAM,CAM/C,GAJEA,EAAU,cAAcN,CAA4B,IAAM,MAC1D,MAAM,KAAKM,EAAU,iBAAiB,QAAQ,CAAC,EAAE,KAC9CE,GAAMP,EAAe,KAAKO,EAAE,aAAe,EAAE,CAChD,EACgB,CAChBD,EAAiBD,EACjB,KACF,CACAA,EAAYA,EAAU,aACxB,CAEA,QAAQ,IAAI,6CAA6ChC,EAAG,EAAE,gBAAgBmC,EAAW,QAAQ,YAAY,CAAC,2BAA4BF,CAAc,EACnJH,EAAO,IAAIG,CAAc,GAAGH,EAAO,IAAIG,EAAgB,CAAC,CAAC,EAC9DH,EAAO,IAAIG,CAAc,EAAG,KAAKjC,CAAE,CACrC,CAEA,QAAQ,IAAI,yBAAyB8B,EAAO,IAAI,iBAAiB,EAEjE,OAAW,CAACE,EAAWI,CAAM,IAAKN,EAAQ,CAGxC,IAAMO,EAAgB,MAAM,KAC1BL,EAAU,iBAAuDP,CAAmB,CACtF,EAAE,OAAQS,GAAM,CACd,IAAMzC,EAAIyC,EAAE,sBAAsB,EAClC,OAAOzC,EAAE,MAAQ,GAAKA,EAAE,OAAS,CACnC,CAAC,EAEG6C,EACDD,EAAcA,EAAc,OAAS,CAAC,GAA8C,KAMvF,GAAI,CAACC,EAAW,CACd,IAAMC,EAAqB,MAAM,KAC/BP,EAAU,iBAAuDN,CAA4B,CAC/F,EAAE,OAAQQ,GAAM,CACd,IAAMzC,EAAIyC,EAAE,sBAAsB,EAClC,OAAOzC,EAAE,MAAQ,GAAKA,EAAE,OAAS,GAAMyC,EAAwB,QACjE,CAAC,EACDI,EAAaC,EAAmBA,EAAmB,OAAS,CAAC,GAA8C,KACvGD,GAAW,QAAQ,IAAI,qEAAqEA,EAAU,aAAa,KAAK,CAAC,GAAG,CAClI,CAOA,GAAI,CAACA,EAAW,CACd,IAAME,EAAgB,MAAM,KAC1BR,EAAU,iBAA8B,yBAAyB,CACnE,EAAE,OAAQE,GAAM,CACd,IAAMzC,EAAIyC,EAAE,sBAAsB,EAClC,OAAOzC,EAAE,MAAQ,GAAKA,EAAE,OAAS,GAC/B,CAAEyC,EAAwB,UAC1BA,EAAE,aAAa,eAAe,IAAM,QACpCP,EAAe,KAAKO,EAAE,aAAe,EAAE,CAC3C,CAAC,EACDI,EAAaE,EAAcA,EAAc,OAAS,CAAC,GAAkC,KACjFF,GAAW,QAAQ,IAAI,kEAAkEA,EAAU,aAAa,KAAK,CAAC,GAAG,CAC/H,CAQA,GAAI,CAACA,EAAW,CACd,IAAMG,EAAST,EAAU,QAAQ,sCAAsC,EACvE,GAAIS,EAAQ,CACV,IAAMC,EAAgB,MAAM,KAC1BD,EAAO,iBAA8B,yBAAyB,CAChE,EAAE,OAAQP,GAAM,CACd,IAAMzC,EAAIyC,EAAE,sBAAsB,EAClC,OAAOzC,EAAE,MAAQ,GAAKA,EAAE,OAAS,GAAKkC,EAAe,KAAKO,EAAE,aAAe,EAAE,CAC/E,CAAC,EACD,QAAQ,IAAI,6DACVQ,EAAc,IAAIR,GAAK,IAAIA,EAAE,aAAa,KAAK,EAAE,MAAM,EAAG,EAAE,CAAC,cAAeA,EAAwB,QAAQ,kBAAkBA,EAAE,aAAa,eAAe,CAAC,EAAE,CAAC,EAElK,IAAMS,EAAeD,EAAc,OAChCR,GAAOA,EAAwB,UAAYA,EAAE,aAAa,eAAe,IAAM,MAClF,EAEMU,EAAcF,EAAc,OAC/BR,GAAM,CAAEA,EAAwB,UAAYA,EAAE,aAAa,eAAe,IAAM,MACnF,EACMW,EAAaF,EAAa,OAAS,EAAIA,EAAeC,EAC5DN,EAAaO,EAAWA,EAAW,OAAS,CAAC,GAAkC,KAC3EP,GAAW,QAAQ,IAAI,+DAA+DA,EAAU,aAAa,KAAK,EAAE,MAAM,EAAG,EAAE,CAAC,cAAeA,EAAgC,QAAQ,kBAAkBA,EAAU,aAAa,eAAe,CAAC,EAAE,CACxP,CACF,CAIA,GAAI,CAACA,EAAW,CACd,IAAMQ,EAAW,MAAM,KACrB,SAAS,iBAA8B,yBAAyB,CAClE,EAAE,OAAQZ,GAAM,CACd,IAAMzC,EAAIyC,EAAE,sBAAsB,EAClC,OAAOzC,EAAE,MAAQ,GAAKA,EAAE,OAAS,GAC/ByC,EAAE,aAAa,eAAe,IAAM,QACpCP,EAAe,KAAKO,EAAE,aAAe,EAAE,CAC3C,CAAC,EACDI,EAAaQ,EAASA,EAAS,OAAS,CAAC,GAAkC,KACvER,GAAW,QAAQ,IAAI,kEAAkEA,EAAU,aAAa,KAAK,CAAC,GAAG,CAC/H,CAEA,QAAQ,IAAI,iDAAkDA,EAAY,IAAIA,EAAU,aAAa,KAAK,CAAC,cAAeA,EAAgC,QAAQ,GAAK,MAAM,EAE7K,IAAMxE,EAAWiF,GAAwBf,EAAWI,EAAQE,CAAS,EAErE,GAAIvF,EAA0B,IAAIe,EAAS,IAAI,EAAG,CAChD,QAAQ,IAAI,0BAA0BA,EAAS,IAAI,gCAAgC,EACnF,QACF,CACA,IAAMkF,EAAa9F,GAAqBY,EAAS,IAAI,EACjDkF,IAAelF,EAAS,MAAQxB,EAAO,OACzC,QAAQ,KAAK,8CAA8CwB,EAAS,IAAI,iBAAiBkF,CAAU,GAAG,EAExGlF,EAAS,KAAOkF,EAChB,QAAQ,IAAI,+BAA+BlF,EAAS,IAAI,iBAAkB,OAAO,KAAKA,EAAS,YAAY,UAAU,CAAC,EAGtH,IAAMmF,EAAsD,CAAC,EACvDC,EAAcpF,EAAS,YAAY,WAGnCqF,EAAa,mBACnB,QAAWnD,KAAMoC,EAAQ,CACvB,IAAMgB,EAAKpD,EAAG,IAAM,CAACmD,EAAW,KAAKnD,EAAG,EAAE,EAAIA,EAAG,GAAK,KAGhDqD,EACHrD,EAAwB,MACzBA,EAAG,aAAa,MAAM,GACrBA,EAAmB,QAAQ,YAC5BoD,GACApD,EAAG,aAAa,YAAY,GAC5BA,EAAG,aAAa,aAAa,GAC7B,KACIsD,EAAUD,EACZA,EAAI,YAAY,EAAE,QAAQ,cAAe,GAAG,EAAE,QAAQ,WAAY,EAAE,EAAE,MAAM,EAAG,EAAE,EACjF,KACEE,EAAU,CAAC,EAAED,GAAWJ,EAAYI,CAAO,GACjD,QAAQ,IAAI,sCAAuCtD,EAAwB,MAAQ,EAAE,SAASA,EAAG,EAAE,cAAcqD,CAAG,cAAcC,CAAO,aAAaC,CAAO,EAAE,EAC3JA,GACFN,EAAW,KAAK,CAAE,IAAKK,EAAU,GAAAtD,CAAG,CAAC,CAEzC,CAIA,GAFA,QAAQ,IAAI,yBAAyBiD,EAAW,MAAM,IAAIb,EAAO,MAAM,iCAAiC,EAEpGa,EAAW,SAAW,EAAG,CAC3B,QAAQ,IAAI,yCAAyCnF,EAAS,IAAI,0CAAqC,EACvG,QACF,CAEA,IAAM1B,EAAW0B,EAAS,KACpBI,EAAU,MACdsF,EACAC,IACgE,CAChE,QAAQ,IAAI,uCAAuCrH,CAAQ,YAAaoH,CAAM,EAC9E,QAAQ,IAAI,4CAA6CP,EAAW,IAAIS,GAAKA,EAAE,GAAG,CAAC,EAEnF,OAAW,CAAE,IAAAL,EAAK,GAAArD,CAAG,IAAKiD,EACpBO,EAAOH,CAAG,IAAM,QAClB,QAAQ,IAAI,8CAA8CA,CAAG,WAAYG,EAAOH,CAAG,EAAG,WAAYrD,CAAE,EAGhGA,EAAG,aAAa,MAAM,IAAM,YAAcA,EAAG,QAAQ,YAAY,IAAM,SACzE,MAAM2D,GAAmB3D,EAAIwD,EAAOH,CAAG,CAAC,EAExCO,GAAY5D,EAAIwD,EAAOH,CAAG,CAAC,EAE7B,QAAQ,IAAI,2DAA6DrD,EAAwB,KAAK,GAEtG,QAAQ,IAAI,sCAAsCqD,CAAG,2BAA2B,EAYpF,GATA,OAAO,cAAc,IAAI,YAAY,gBAAiB,CAAE,OAAQ,CAAE,SAAAjH,CAAS,CAAE,CAAC,CAAC,EAS3E,EANFE,EAAO,YACP,CAAC,CAACgG,GAAW,aAAa,gBAAgB,GACzCA,aAAqB,aAAeA,EAAU,QAAQ,mBAAwB,QAC/EN,EAAU,aAAa,gBAAgB,GACtCA,aAAqB,aAAeA,EAAU,QAAQ,mBAAwB,QAG/E,eAAQ,IAAI,mFAAmF,EACxF,CAAE,QAAS,CAAC,CAAE,KAAM,OAAQ,KAAM,iCAAkC,CAAC,CAAE,EAMhF,QAAQ,IAAI,qEAAqE,EACjF,IAAI6B,EAAiE,KAGrE,GAAIvB,GAAa,SAAS,SAASA,CAAS,EAAG,CAC7C,IAAMwB,EAAY,CAAExB,EAAgC,UAClDA,EAAU,aAAa,eAAe,IAAM,OACxC7C,EAAI6C,EAAU,sBAAsB,EACtCwB,GAAarE,EAAE,MAAQ,GAAKA,EAAE,OAAS,IACzCoE,EAAMvB,EACN,QAAQ,IAAI,+DAA+DuB,EAAI,aAAa,KAAK,CAAC,GAAG,EAEzG,CAEA,GAAI,CAACA,EAAK,CAGR,IAAME,EAAW,KAAK,IAAI,EAAI,IAC9B,KAAO,KAAK,IAAI,EAAIA,GAAU,CAC5B,IAAMC,EAAa,MAAM,KACvBhC,EAAU,iBAAuDP,CAAmB,CACtF,EAAE,OAAQS,GAAM,CACd,IAAMzC,GAAIyC,EAAE,sBAAsB,EAClC,OAAOzC,GAAE,MAAQ,GAAKA,GAAE,OAAS,CACnC,CAAC,EACKwE,EAAOD,EAAWA,EAAW,OAAS,CAAC,GAAK,KAClD,GAAIC,EAAM,CAAEJ,EAAMI,EAAM,KAAO,CAC/B,MAAM,IAAI,QAAexE,GAAM,WAAWA,EAAG,GAAG,CAAC,CACnD,CACF,CAGA,GAAI,CAACoE,EAAK,CACR,IAAMK,EAAW,MAAM,MACpBlC,IAAc,SAAS,KAAOA,EAAY,UAAU,iBAA8B,yBAAyB,CAC9G,EAAE,OAAQE,GAAM,CACd,IAAMzC,EAAIyC,EAAE,sBAAsB,EAClC,OAAOzC,EAAE,MAAQ,GAAKA,EAAE,OAAS,GAC/B,CAAEyC,EAAwB,UAC1BA,EAAE,aAAa,eAAe,IAAM,QACpCP,EAAe,KAAKO,EAAE,aAAe,EAAE,CAC3C,CAAC,EACD2B,EAAMK,EAASA,EAAS,OAAS,CAAC,GAAK,KACnCL,GAAK,QAAQ,IAAI,qEAAqEA,EAAI,aAAa,KAAK,CAAC,GAAG,CACtH,CAEA,OAAKA,GAKL,QAAQ,IAAI,yDAA0DA,EAAoB,aAAa,KAAK,CAAC,GAAG,EAC/GA,EAAoB,MAAM,EACpB,CAAE,QAAS,CAAC,CAAE,KAAM,OAAQ,KAAM,mCAAoC,CAAC,CAAE,IAN9E,QAAQ,KAAK,qEAAqE,EAC3E,CAAE,QAAS,CAAC,CAAE,KAAM,OAAQ,KAAM,iHAAkH,CAAC,CAAE,EAMlK,EAEA,GAAI,CACF,IAAMM,EASF,CACF,KAAMrG,EAAS,KACf,YAAaA,EAAS,YACtB,YAAaA,EAAS,YACtB,QAAAI,CACF,EACIJ,EAAS,aAAe,OAAO,KAAKA,EAAS,WAAW,EAAE,OAAS,IACrEqG,EAAQ,YAAcrG,EAAS,aAEjC,MAAM,UAAU,aAAc,aAAaqG,CAAO,EAClDpH,EAA0B,IAAIe,EAAS,IAAI,EAI3C,IAAMU,EAAgB,OAA8C,sBAA2B,CAAC,EAChGA,EAAYV,EAAS,IAAI,EAAIwE,EACzBhG,EAAO,OACT,QAAQ,IAAI,yCAAyCwB,EAAS,IAAI,GAAIA,CAAQ,CAElF,MAAQ,CAER,CACF,CACF,CAMA,SAASG,GAAgBjB,EAAcoH,EAA2B,CAC5D,6BAA6B,KAAKpH,CAAI,GACxC,QAAQ,KAAK,uBAAuBA,CAAI,iFAAiF,GAEvH,CAACoH,GAAeA,IAAgB,gBAClC,QAAQ,KAAK,uBAAuBpH,CAAI,kCAAkC,EAExE,qCAAqC,KAAKoH,CAAW,GACvD,QAAQ,KAAK,uBAAuBpH,CAAI,sGAAsG,CAElJ,CAEA,eAAsBqH,GAAe/H,EAAuC,CACtE,SAAS,aAAe,WAC1B,MAAM,IAAI,QAAegI,GACvB,SAAS,iBAAiB,mBAAoB,IAAMA,EAAQ,EAAG,CAAE,KAAM,EAAK,CAAC,CAC/E,EAGFhG,GAAsB,EACtBvB,EAA0B,MAAM,EAChCqD,GAAc9D,CAAM,EACpBqE,GAAsBrE,CAAM,EAC5B,MAAMsE,EAAUtE,CAAM,EAItB,MAAM4C,GAAiB5C,CAAM,CAC/B,CAEO,SAASiI,IAAsB,CACpC7F,GAAU,WAAW,EACrBA,EAAW,IACb,CNlzBA,eAAsB8F,GAAWC,EAAsD,CACrF,IAAMC,EAAWC,GAAcF,CAAM,EAErC,OAAIC,EAAS,OACX,QAAQ,MAAM,6BAA8B,CAC1C,gBAAiBE,EAAkB,EACnC,OAAQF,CACV,CAAC,EAGH,MAAMG,GAAeH,CAAQ,EAEtB,CACL,QAAS,SAAY,CACnBI,GAAc,EACd,MAAMC,GAAc,CACtB,EACA,SAAUC,EACV,YAAaJ,EAAkB,CACjC,CACF,CAWE,OAAO,OAAW,KAClB,CAAE,OAA8C,2BAE3CJ,GAAW",
|
|
6
|
-
"names": ["src_exports", "__export", "autoWebMCP", "resolveConfig", "userConfig", "strict", "enableAliasResolution", "ARIA_ROLES_TO_SCAN", "inputTypeToSchema", "input", "mapInputElement", "mapSelectElement", "buildStringSchema", "prop", "listId", "datalist", "options", "o", "PLACEHOLDER_PATTERNS", "isPlaceholderOption", "opt", "select", "enumValues", "oneOf", "child", "groupLabel", "entry", "collectCheckboxEnum", "form", "name", "el", "cb", "v", "collectRadioEnum", "r", "collectRadioOneOf", "title", "getRadioLabelText", "ariaRoleToSchema", "role", "min", "max", "ownedId", "listbox", "radio", "parent", "clone", "text", "label", "formIndex", "analyzeForm", "form", "override", "name", "inferToolName", "description", "inferToolDescription", "inputSchema", "fieldElements", "buildSchema", "annotations", "inferAnnotations", "nativeName", "sanitizeName", "explicit", "submitText", "getSubmitButtonText", "heading", "getNearestHeadingText", "segment", "getLastPathSegment", "formIndex", "raw", "buttons", "btn", "text", "node", "sibling", "url", "segments", "nativeDesc", "legend", "ariaLabel", "describedById", "descEl", "pageTitle", "READONLY_BUTTON_PATTERNS", "DESTRUCTIVE_BUTTON_PATTERNS", "DESTRUCTIVE_URL_PATTERNS", "isGet", "isReadLabel", "isDestructiveLabel", "isDestructiveUrl", "extractDefaultValue", "control", "type", "selected", "o", "collectShadowControls", "root", "visited", "results", "el", "found", "f", "collectFormAssociatedControls", "controls", "seen", "shadowControl", "properties", "required", "processedRadioGroups", "processedCheckboxGroups", "fieldKey", "resolveNativeControlFallbackKey", "schemaProp", "inputTypeToSchema", "isControlVisible", "inferFieldTitle", "desc", "inferFieldDescription", "defaultVal", "collectRadioEnum", "radioOneOf", "collectRadioOneOf", "checkedRadio", "checkboxValues", "collectCheckboxEnum", "arrayProp", "checkedBoxes", "
|
|
4
|
+
"sourcesContent": ["/**\n * index.ts \u2014 Entry point & public API for auto-webmcp\n *\n * Zero-config drop-in:\n * <script src=\"auto-webmcp.iife.js\"></script>\n *\n * ESM usage:\n * import { autoWebMCP } from 'auto-webmcp';\n * autoWebMCP({ exclude: ['#login-form'] });\n */\n\nimport { AutoWebMCPConfig, resolveConfig } from './config.js';\nimport { startDiscovery, stopDiscovery } from './discovery.js';\nimport { unregisterAll, getAllRegisteredTools, isWebMCPSupported } from './registry.js';\n\nexport type { AutoWebMCPConfig } from './config.js';\nexport type { ToolMetadata } from './analyzer.js';\nexport type { JsonSchema, JsonSchemaProperty } from './schema.js';\n\nexport interface AutoWebMCPHandle {\n /** Stop observing and unregister all tools */\n destroy: () => Promise<void>;\n /** Return all currently registered tools */\n getTools: () => Array<{ form: HTMLFormElement; name: string }>;\n /** True if running in a WebMCP-capable browser */\n isSupported: boolean;\n}\n\n/**\n * Initialize auto-webmcp.\n *\n * @param config \u2014 Optional configuration (all fields optional)\n * @returns A handle to inspect or tear down the instance\n */\nexport async function autoWebMCP(config?: AutoWebMCPConfig): Promise<AutoWebMCPHandle> {\n const resolved = resolveConfig(config);\n\n if (resolved.debug) {\n console.debug('[auto-webmcp] Initializing', {\n webmcpSupported: isWebMCPSupported(),\n config: resolved,\n });\n }\n\n await startDiscovery(resolved);\n\n return {\n destroy: async () => {\n stopDiscovery();\n await unregisterAll();\n },\n getTools: getAllRegisteredTools,\n isSupported: isWebMCPSupported(),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Auto-init for IIFE / script-tag usage\n// ---------------------------------------------------------------------------\n\n// When loaded as a <script> tag, auto-initialize with zero config.\n// Users can prevent this by setting window.__AUTO_WEBMCP_NO_AUTOINIT = true\n// before the script loads.\n\nif (\n typeof window !== 'undefined' &&\n !(window as unknown as Record<string, unknown>)['__AUTO_WEBMCP_NO_AUTOINIT']\n) {\n void autoWebMCP();\n}\n", "/**\n * config.ts \u2014 User configuration merging & defaults\n */\n\nexport interface FormOverride {\n name?: string;\n description?: string;\n}\n\nexport interface AutoWebMCPConfig {\n /**\n * CSS selectors for forms to skip. E.g. ['#login-form', '[data-no-webmcp]']\n */\n exclude?: string[];\n\n /**\n * If true, agent-invoked forms are auto-submitted without human confirmation.\n * Default: false\n */\n autoSubmit?: boolean;\n\n /**\n * How to handle forms that already use native declarative WebMCP attributes.\n * - skip: do not imperatively register forms with toolname\n * - augment: reserved for future metadata-only enhancement (currently behaves like skip)\n * - force: always register imperatively even when toolname exists\n * Default: skip\n */\n declarativeMode?: 'skip' | 'augment' | 'force';\n\n /**\n * Parameter binding behavior for execute() input keys.\n */\n paramBinding?: {\n /**\n * If true, resolve semantically similar parameter keys to schema keys.\n * Default: true\n */\n enableAliasResolution?: boolean;\n /**\n * If true, only exact schema keys are accepted (disables alias resolution).\n * Default: false\n */\n strict?: boolean;\n };\n\n /**\n * Execution behavior controls.\n */\n execution?: {\n /**\n * Max time to wait for submit/result before returning a deterministic timeout state.\n * Default: 15000\n */\n timeoutMs?: number;\n };\n\n /**\n * Per-form name/description overrides keyed by CSS selector.\n */\n overrides?: Record<string, FormOverride>;\n\n /**\n * When true, skip filling any form field that already has a non-empty value.\n * Useful for forms with pre-populated defaults (e.g. Salesforce Lightning) where\n * the agent should only fill blank fields. Default: false\n */\n preserveExisting?: boolean;\n\n /**\n * Log registered tools to console on init. Default: false\n */\n debug?: boolean;\n}\n\nexport interface ResolvedConfig {\n exclude: string[];\n autoSubmit: boolean;\n declarativeMode: 'skip' | 'augment' | 'force';\n paramBinding: {\n enableAliasResolution: boolean;\n strict: boolean;\n };\n execution: {\n timeoutMs: number;\n };\n overrides: Record<string, FormOverride>;\n preserveExisting: boolean;\n debug: boolean;\n}\n\nexport function resolveConfig(userConfig?: AutoWebMCPConfig): ResolvedConfig {\n const strict = userConfig?.paramBinding?.strict ?? false;\n const enableAliasResolution =\n strict ? false : (userConfig?.paramBinding?.enableAliasResolution ?? true);\n return {\n exclude: userConfig?.exclude ?? [],\n autoSubmit: userConfig?.autoSubmit ?? false,\n declarativeMode: userConfig?.declarativeMode ?? 'skip',\n paramBinding: {\n strict,\n enableAliasResolution,\n },\n execution: {\n timeoutMs: Math.max(100, userConfig?.execution?.timeoutMs ?? 15000),\n },\n overrides: userConfig?.overrides ?? {},\n preserveExisting: userConfig?.preserveExisting ?? false,\n debug: userConfig?.debug ?? false,\n };\n}\n", "/**\n * schema.ts \u2014 HTML input type \u2192 JSON Schema type mapping\n */\n\nexport const ARIA_ROLES_TO_SCAN = [\n 'textbox', 'combobox', 'checkbox', 'radio', 'switch',\n 'spinbutton', 'searchbox', 'slider',\n] as const;\n\nexport type AriaRole = typeof ARIA_ROLES_TO_SCAN[number];\n\nexport interface JsonSchemaProperty {\n type: string;\n format?: string;\n description?: string;\n title?: string;\n enum?: string[];\n oneOf?: Array<{ const: string; title: string; group?: string }>;\n items?: { type: string; enum?: string[] };\n minimum?: number;\n maximum?: number;\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n default?: unknown;\n}\n\nexport interface JsonSchema {\n $schema?: string;\n type: 'object';\n properties: Record<string, JsonSchemaProperty>;\n required: string[];\n}\n\n/** Maps an HTML <input type> to a JSON Schema property base */\nexport function inputTypeToSchema(\n input: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement,\n): JsonSchemaProperty | null {\n if (input instanceof HTMLInputElement) {\n return mapInputElement(input);\n }\n if (input instanceof HTMLTextAreaElement) {\n return { type: 'string' };\n }\n if (input instanceof HTMLSelectElement) {\n return mapSelectElement(input);\n }\n return null;\n}\n\nfunction mapInputElement(input: HTMLInputElement): JsonSchemaProperty | null {\n const type = input.type.toLowerCase();\n\n switch (type) {\n case 'text':\n case 'search':\n case 'tel':\n return buildStringSchema(input);\n\n case 'email':\n return { ...buildStringSchema(input), format: 'email' };\n\n case 'url':\n return { ...buildStringSchema(input), format: 'uri' };\n\n case 'number':\n case 'range': {\n const prop: JsonSchemaProperty = { type: 'number' };\n if (input.min !== '') prop.minimum = parseFloat(input.min);\n if (input.max !== '') prop.maximum = parseFloat(input.max);\n return prop;\n }\n\n case 'date':\n return { type: 'string', format: 'date' };\n\n case 'datetime-local':\n return { type: 'string', format: 'date-time' };\n\n case 'time':\n return { type: 'string', format: 'time' };\n\n case 'month':\n return { type: 'string', pattern: '^\\\\d{4}-\\\\d{2}$' };\n\n case 'week':\n return { type: 'string', pattern: '^\\\\d{4}-W\\\\d{2}$' };\n\n case 'color':\n return { type: 'string', pattern: '^#[0-9a-fA-F]{6}$' };\n\n case 'checkbox':\n return { type: 'boolean' };\n\n case 'radio':\n // Radio groups are handled at the form level in analyzer.ts\n return { type: 'string' };\n\n case 'file':\n case 'hidden':\n case 'submit':\n case 'reset':\n case 'button':\n case 'image':\n // These are not exposed to agents\n return null;\n\n case 'password':\n // Skip passwords \u2014 never expose to agents\n return null;\n\n default:\n return { type: 'string' };\n }\n}\n\nfunction buildStringSchema(input: HTMLInputElement): JsonSchemaProperty {\n const prop: JsonSchemaProperty = { type: 'string' };\n if (input.minLength > 0) prop.minLength = input.minLength;\n if (input.maxLength > 0 && input.maxLength !== 524288) prop.maxLength = input.maxLength;\n if (input.pattern) prop.pattern = input.pattern;\n\n // Expose <datalist> suggestions as enum/oneOf so agents know the preferred values.\n // The field stays type:string because datalist is advisory, not restrictive.\n const listId = input.getAttribute('list');\n if (listId) {\n const datalist = input.ownerDocument.getElementById(listId);\n if (datalist instanceof HTMLDataListElement) {\n const options = Array.from(datalist.options).filter(\n (o) => !o.disabled && o.value.trim() !== '',\n );\n if (options.length > 0) {\n prop.enum = options.map((o) => o.value.trim());\n prop.oneOf = options.map((o) => ({\n const: o.value.trim(),\n title: o.textContent?.trim() || o.value.trim(),\n }));\n }\n }\n }\n\n return prop;\n}\n\n/**\n * Matches common placeholder/prompt option text patterns so that options like\n * \"-- Select a size --\" or \"Choose region\" are excluded from the schema enum\n * while genuinely meaningful empty-value options like \"No preference\" are kept.\n */\nconst PLACEHOLDER_PATTERNS = /^(select|choose|pick)\\b|^--+|---/i;\n\n/**\n * Returns true when an option should be treated as a UI placeholder that agents\n * should not select. An option is a placeholder when it is explicitly disabled,\n * or when its submission value is empty and its visible text matches common\n * prompt patterns (\"Select...\", \"Choose...\", \"Pick...\", \"-- ... --\").\n *\n * Options with empty values that carry real meaning (\"No preference\", \"Any\",\n * \"All Categories\") are NOT considered placeholders and will be included.\n */\nfunction isPlaceholderOption(opt: HTMLOptionElement): boolean {\n if (opt.disabled) return true;\n if (opt.value !== '') return false;\n return PLACEHOLDER_PATTERNS.test(opt.text.trim());\n}\n\nfunction mapSelectElement(select: HTMLSelectElement): JsonSchemaProperty {\n const enumValues: string[] = [];\n const oneOf: Array<{ const: string; title: string; group?: string }> = [];\n\n for (const child of Array.from(select.children)) {\n if (child instanceof HTMLOptGroupElement) {\n if (child.disabled) continue;\n const groupLabel = child.label?.trim() ?? '';\n for (const opt of Array.from(child.children)) {\n if (!(opt instanceof HTMLOptionElement)) continue;\n if (isPlaceholderOption(opt)) continue;\n enumValues.push(opt.value);\n const entry: { const: string; title: string; group?: string } = {\n const: opt.value,\n title: opt.text.trim() || opt.value,\n };\n if (groupLabel) entry.group = groupLabel;\n oneOf.push(entry);\n }\n } else if (child instanceof HTMLOptionElement) {\n if (isPlaceholderOption(child)) continue;\n enumValues.push(child.value);\n oneOf.push({ const: child.value, title: child.text.trim() || child.value });\n }\n }\n\n if (enumValues.length === 0) return { type: 'string' };\n\n if (select.multiple) {\n // Multi-select: agent passes an array of selected values\n return { type: 'array', items: { type: 'string', enum: enumValues } };\n }\n\n return { type: 'string', enum: enumValues, oneOf };\n}\n\n/** Collect all checkbox values for a given name within a form (for checkbox groups) */\nexport function collectCheckboxEnum(form: HTMLFormElement, name: string): string[] {\n return Array.from(form.elements)\n .filter(\n (el): el is HTMLInputElement =>\n el instanceof HTMLInputElement &&\n el.type === 'checkbox' &&\n el.name === name,\n )\n .map((cb) => cb.value)\n .filter((v) => v !== '' && v !== 'on'); // 'on' is the browser default when no value attr is set\n}\n\n/** Collect all radio button values for a given name within a form */\nexport function collectRadioEnum(form: HTMLFormElement, name: string): string[] {\n const radios = Array.from(form.elements).filter(\n (el): el is HTMLInputElement =>\n el instanceof HTMLInputElement &&\n el.type === 'radio' &&\n el.name === name,\n );\n return radios.map((r) => r.value).filter((v) => v !== '');\n}\n\n/** Collect radio button values + label titles as oneOf entries */\nexport function collectRadioOneOf(\n form: HTMLFormElement,\n name: string,\n): Array<{ const: string; title: string }> {\n const radios = Array.from(form.elements)\n .filter(\n (el): el is HTMLInputElement =>\n el instanceof HTMLInputElement &&\n el.type === 'radio' &&\n el.name === name,\n )\n .filter((r) => r.value !== '');\n\n return radios.map((r) => {\n const title = getRadioLabelText(r);\n return { const: r.value, title: title || r.value };\n });\n}\n\n/** Maps an ARIA role element to a JSON Schema property */\nexport function ariaRoleToSchema(el: Element, role: AriaRole): JsonSchemaProperty {\n switch (role) {\n case 'checkbox':\n case 'switch':\n return { type: 'boolean' };\n\n case 'spinbutton':\n case 'slider': {\n const prop: JsonSchemaProperty = { type: 'number' };\n const min = el.getAttribute('aria-valuemin');\n const max = el.getAttribute('aria-valuemax');\n if (min !== null) prop.minimum = parseFloat(min);\n if (max !== null) prop.maximum = parseFloat(max);\n return prop;\n }\n\n case 'combobox': {\n const ownedId = el.getAttribute('aria-owns') ?? el.getAttribute('aria-controls');\n if (ownedId) {\n const listbox = document.getElementById(ownedId);\n if (listbox) {\n const options = Array.from(listbox.querySelectorAll('[role=\"option\"]')).filter(\n (o) => o.getAttribute('aria-disabled') !== 'true',\n );\n if (options.length > 0) {\n const enumValues = options\n .map((o) => (o.getAttribute('data-value') ?? o.textContent ?? '').trim())\n .filter(Boolean);\n const oneOf = options.map((o) => ({\n const: (o.getAttribute('data-value') ?? o.textContent ?? '').trim(),\n title: (o.textContent ?? '').trim(),\n }));\n return { type: 'string', enum: enumValues, oneOf };\n }\n }\n }\n return { type: 'string' };\n }\n\n case 'textbox':\n case 'searchbox':\n case 'radio':\n default:\n return { type: 'string' };\n }\n}\n\nfunction getRadioLabelText(radio: HTMLInputElement): string {\n // 1. Wrapping label\n const parent = radio.closest('label');\n if (parent) {\n const clone = parent.cloneNode(true) as HTMLLabelElement;\n clone.querySelectorAll('input, select, textarea, button').forEach((el) => el.remove());\n const text = clone.textContent?.trim() ?? '';\n if (text) return text;\n }\n // 2. Label pointing to radio by id\n if (radio.id) {\n const label = document.querySelector<HTMLLabelElement>(`label[for=\"${CSS.escape(radio.id)}\"]`);\n if (label) {\n const text = label.textContent?.trim() ?? '';\n if (text) return text;\n }\n }\n return '';\n}\n", "/**\n * analyzer.ts \u2014 Infer tool name, description, and JSON Schema from form DOM\n */\n\nimport { JsonSchema, JsonSchemaProperty, inputTypeToSchema, collectRadioEnum, collectRadioOneOf, collectCheckboxEnum, ARIA_ROLES_TO_SCAN, AriaRole, ariaRoleToSchema } from './schema.js';\nimport { FormOverride } from './config.js';\n\nexport interface ToolAnnotations {\n readOnlyHint?: boolean;\n destructiveHint?: boolean;\n idempotentHint?: boolean;\n openWorldHint?: boolean;\n}\n\nexport interface ToolMetadata {\n name: string;\n description: string;\n inputSchema: JsonSchema;\n annotations?: ToolAnnotations;\n /** Key \u2192 DOM element for fields not addressable by name (id-keyed or ARIA-role controls). */\n fieldElements?: Map<string, Element>;\n}\n\n// Track form index for fallback naming\nlet formIndex = 0;\n\n/** Reset form index counter (useful in tests) */\nexport function resetFormIndex(): void {\n formIndex = 0;\n}\n\n/** Derive ToolMetadata from a <form> element */\nexport function analyzeForm(form: HTMLFormElement, override?: FormOverride): ToolMetadata {\n const name = override?.name ?? inferToolName(form);\n const description = override?.description ?? inferToolDescription(form);\n const { schema: inputSchema, fieldElements } = buildSchema(form);\n const annotations = inferAnnotations(form);\n\n return { name, description, inputSchema, annotations, fieldElements };\n}\n\n// ---------------------------------------------------------------------------\n// Tool name inference\n// ---------------------------------------------------------------------------\n\nfunction inferToolName(form: HTMLFormElement): string {\n // 1. Native toolname attribute (spec)\n const nativeName = form.getAttribute('toolname');\n if (nativeName) return sanitizeName(nativeName);\n\n // 2. Explicit data attribute\n const explicit = form.dataset['webmcpName'];\n if (explicit) return sanitizeName(explicit);\n\n // 2. Submit button text\n const submitText = getSubmitButtonText(form);\n if (submitText) return sanitizeName(submitText);\n\n // 3. Nearest heading above the form\n const heading = getNearestHeadingText(form);\n if (heading) return sanitizeName(heading);\n\n // 4. Form id or name attribute\n if (form.id) return sanitizeName(form.id);\n if (form.name) return sanitizeName(form.name);\n\n // 5. Form action URL path segment\n if (form.action) {\n const segment = getLastPathSegment(form.action);\n if (segment) return sanitizeName(segment);\n }\n\n // 6. Fallback\n return `form_${++formIndex}`;\n}\n\nfunction sanitizeName(raw: string): string {\n return raw\n .toLowerCase()\n .trim()\n .replace(/[^a-z0-9]+/g, '_')\n .replace(/^_+|_+$/g, '')\n .slice(0, 64) || 'form';\n}\n\nfunction getSubmitButtonText(form: HTMLFormElement): string {\n const buttons = [\n ...Array.from(form.querySelectorAll<HTMLButtonElement>('button[type=\"submit\"], button:not([type])')),\n ...Array.from(form.querySelectorAll<HTMLInputElement>('input[type=\"submit\"]')),\n ];\n\n for (const btn of buttons) {\n const text =\n btn instanceof HTMLInputElement\n ? btn.value.trim()\n : btn.textContent?.trim() ?? '';\n if (text && text.length > 0 && text.length < 80) return text;\n }\n return '';\n}\n\nfunction getNearestHeadingText(form: HTMLFormElement): string {\n // Walk up the DOM looking for a preceding sibling or parent heading\n let node: Element | null = form;\n while (node) {\n // Check preceding siblings\n let sibling = node.previousElementSibling;\n while (sibling) {\n if (/^H[1-3]$/i.test(sibling.tagName)) {\n const text = sibling.textContent?.trim() ?? '';\n if (text) return text;\n }\n sibling = sibling.previousElementSibling;\n }\n node = node.parentElement;\n // Stop at body\n if (!node || node === document.body) break;\n }\n return '';\n}\n\nfunction getLastPathSegment(url: string): string {\n try {\n const parsed = new URL(url, window.location.href);\n const segments = parsed.pathname.split('/').filter(Boolean);\n return segments[segments.length - 1] ?? '';\n } catch {\n return '';\n }\n}\n\n// ---------------------------------------------------------------------------\n// Tool description inference\n// ---------------------------------------------------------------------------\n\nfunction inferToolDescription(form: HTMLFormElement): string {\n // 1. Native tooldescription attribute (spec)\n const nativeDesc = form.getAttribute('tooldescription');\n if (nativeDesc) return nativeDesc.trim();\n\n // 2. Explicit data attribute\n const explicit = form.dataset['webmcpDescription'];\n if (explicit) return explicit.trim();\n\n // 2. <legend> inside the form\n const legend = form.querySelector('legend');\n if (legend?.textContent?.trim()) return legend.textContent.trim();\n\n // 3. aria-label on the form\n const ariaLabel = form.getAttribute('aria-label');\n if (ariaLabel?.trim()) return ariaLabel.trim();\n\n // 4. aria-describedby\n const describedById = form.getAttribute('aria-describedby');\n if (describedById) {\n const descEl = document.getElementById(describedById);\n if (descEl?.textContent?.trim()) return descEl.textContent.trim();\n }\n\n // 5. Combine nearest heading + page title\n const heading = getNearestHeadingText(form);\n const pageTitle = document.title?.trim();\n if (heading && pageTitle) return `${heading}: ${pageTitle}`;\n if (heading) return heading;\n if (pageTitle) return pageTitle;\n\n return 'Submit form';\n}\n\n// ---------------------------------------------------------------------------\n// Tool annotations inference\n// ---------------------------------------------------------------------------\n\nconst READONLY_BUTTON_PATTERNS = /^(search|find|look|filter|browse|view|show|check|preview|get|fetch|retrieve|load)\\b/i;\nconst DESTRUCTIVE_BUTTON_PATTERNS = /^(delete|remove|cancel|terminate|destroy|purge|revoke|unsubscribe|deactivate)\\b/i;\nconst DESTRUCTIVE_URL_PATTERNS = /\\/(delete|remove|cancel|destroy)\\b/i;\n\nfunction inferAnnotations(form: HTMLFormElement): ToolAnnotations {\n const annotations: ToolAnnotations = {};\n\n // Manual overrides via data-webmcp-* attributes take highest priority\n if (form.dataset['webmcpReadonly'] !== undefined) {\n annotations.readOnlyHint = form.dataset['webmcpReadonly'] !== 'false';\n }\n if (form.dataset['webmcpDestructive'] !== undefined) {\n annotations.destructiveHint = form.dataset['webmcpDestructive'] !== 'false';\n }\n if (form.dataset['webmcpIdempotent'] !== undefined) {\n annotations.idempotentHint = form.dataset['webmcpIdempotent'] !== 'false';\n }\n if (form.dataset['webmcpOpenworld'] !== undefined) {\n annotations.openWorldHint = form.dataset['webmcpOpenworld'] !== 'false';\n }\n\n // readOnlyHint: GET method or common read-action button text\n if (annotations.readOnlyHint === undefined) {\n const isGet = form.method.toLowerCase() === 'get';\n const submitText = getSubmitButtonText(form);\n const isReadLabel = submitText ? READONLY_BUTTON_PATTERNS.test(submitText.trim()) : false;\n if (isGet || isReadLabel) annotations.readOnlyHint = true;\n }\n\n // destructiveHint: common delete/cancel button text or URL path\n if (annotations.destructiveHint === undefined) {\n const submitText = getSubmitButtonText(form);\n const isDestructiveLabel = submitText ? DESTRUCTIVE_BUTTON_PATTERNS.test(submitText.trim()) : false;\n const isDestructiveUrl = form.action ? DESTRUCTIVE_URL_PATTERNS.test(form.action) : false;\n if (isDestructiveLabel || isDestructiveUrl) annotations.destructiveHint = true;\n }\n\n // idempotentHint: read-only forms and GET requests are naturally idempotent\n if (annotations.idempotentHint === undefined) {\n if (annotations.readOnlyHint === true || form.method.toLowerCase() === 'get') {\n annotations.idempotentHint = true;\n }\n }\n\n // openWorldHint: forms that write/mutate data interact with external entities\n if (annotations.openWorldHint === undefined) {\n annotations.openWorldHint = annotations.readOnlyHint !== true;\n }\n\n // Return empty object if no non-default hints were inferred (avoids polluting registrations)\n const hasNonDefault =\n annotations.readOnlyHint === true ||\n annotations.destructiveHint === true ||\n annotations.idempotentHint === true ||\n annotations.openWorldHint === false;\n\n return hasNonDefault ? annotations : {};\n}\n\n// ---------------------------------------------------------------------------\n// JSON Schema construction\n// ---------------------------------------------------------------------------\n\n/**\n * Returns the current DOM value of a control as a JSON Schema `default`, or\n * `undefined` if the control is empty/unchecked (no useful default to expose).\n * Number and range inputs are returned as numbers, not strings.\n */\nfunction extractDefaultValue(\n control: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement,\n): unknown {\n if (control instanceof HTMLInputElement) {\n const type = control.type.toLowerCase();\n if (type === 'checkbox') return control.checked ? true : undefined;\n if (type === 'radio') return undefined; // handled at the radio-group level\n if (type === 'number' || type === 'range') {\n return control.value !== '' ? parseFloat(control.value) : undefined;\n }\n return control.value !== '' ? control.value : undefined;\n }\n if (control instanceof HTMLTextAreaElement) {\n return control.value !== '' ? control.value : undefined;\n }\n if (control instanceof HTMLSelectElement) {\n if (control.multiple) {\n const selected = Array.from(control.options).filter((o) => o.selected).map((o) => o.value);\n return selected.length > 0 ? selected : undefined;\n }\n return control.value !== '' ? control.value : undefined;\n }\n return undefined;\n}\n\n/**\n * Recursively collect native form controls inside shadow roots.\n * querySelectorAll() does not pierce shadow DOM, so Web Components and\n * design-system elements that render inputs inside their shadow root are\n * invisible to the standard form scan. This helper walks every element,\n * enters its shadowRoot when present, and collects inputs/textareas/selects.\n */\nfunction collectShadowControls(\n root: Element | ShadowRoot,\n visited = new Set<Element | ShadowRoot>(),\n): Array<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement> {\n if (visited.has(root)) return [];\n visited.add(root);\n const results: Array<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement> = [];\n for (const el of Array.from(root.querySelectorAll('*'))) {\n if (el.shadowRoot) {\n const found = Array.from(\n el.shadowRoot.querySelectorAll<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>(\n 'input, textarea, select',\n ),\n );\n if (found.length > 0) {\n console.log(`[auto-webmcp] shadow: found ${found.length} control(s) in ${el.tagName.toLowerCase()} shadow root:`, found.map(f => `${f.tagName.toLowerCase()}[type=${f.type ?? '?'}][name=\"${(f as HTMLInputElement).name}\"][id=\"${f.id}\"]`));\n }\n results.push(...found, ...collectShadowControls(el.shadowRoot, visited));\n }\n }\n return results;\n}\n\n/**\n * Collect native controls associated with a form.\n * Uses form.elements to include controls outside the form subtree that point\n * to this form via the HTML `form` attribute.\n */\nfunction collectFormAssociatedControls(\n form: HTMLFormElement,\n): Array<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement> {\n const controls = Array.from(form.elements).filter(\n (el): el is HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement =>\n el instanceof HTMLInputElement ||\n el instanceof HTMLTextAreaElement ||\n el instanceof HTMLSelectElement,\n );\n\n const seen = new Set<Element>(controls);\n for (const shadowControl of collectShadowControls(form)) {\n if (!seen.has(shadowControl)) {\n controls.push(shadowControl);\n seen.add(shadowControl);\n }\n }\n\n return controls;\n}\n\nfunction buildSchema(form: HTMLFormElement): { schema: JsonSchema; fieldElements: Map<string, Element> } {\n const properties: Record<string, JsonSchemaProperty> = {};\n const required: string[] = [];\n const fieldElements = new Map<string, Element>();\n\n // Track which radio/checkbox group names we've already processed\n const processedRadioGroups = new Set<string>();\n const processedCheckboxGroups = new Set<string>();\n\n const controls = collectFormAssociatedControls(form);\n\n for (const control of controls) {\n const name = control.name;\n const fieldKey = name || resolveNativeControlFallbackKey(control);\n if (!fieldKey) continue;\n\n // Skip already-processed radio groups\n if (control instanceof HTMLInputElement && control.type === 'radio') {\n if (processedRadioGroups.has(fieldKey)) continue;\n processedRadioGroups.add(fieldKey);\n }\n\n // Skip already-processed checkbox groups\n if (control instanceof HTMLInputElement && control.type === 'checkbox') {\n if (processedCheckboxGroups.has(fieldKey)) continue;\n processedCheckboxGroups.add(fieldKey);\n }\n\n const schemaProp = inputTypeToSchema(control);\n if (!schemaProp) continue; // skipped types (hidden, password, file, etc.)\n if (!isControlVisible(control)) continue; // display:none, aria-hidden, disabled fieldset\n\n // Enrich with title and description\n schemaProp.title = inferFieldTitle(control);\n const desc = inferFieldDescription(control);\n if (desc) schemaProp.description = desc;\n\n // Attach current DOM value as JSON Schema 'default' so agents know the pre-filled state\n const defaultVal = extractDefaultValue(control);\n if (defaultVal !== undefined) schemaProp.default = defaultVal;\n\n // For radio groups, add enum, oneOf, and default (currently checked value)\n if (control instanceof HTMLInputElement && control.type === 'radio') {\n schemaProp.enum = collectRadioEnum(form, fieldKey);\n const radioOneOf = collectRadioOneOf(form, fieldKey);\n if (radioOneOf.length > 0) schemaProp.oneOf = radioOneOf;\n const checkedRadio = Array.from(form.elements).find(\n (el): el is HTMLInputElement =>\n el instanceof HTMLInputElement &&\n el.type === 'radio' &&\n el.name === fieldKey &&\n el.checked,\n );\n if (checkedRadio?.value) schemaProp.default = checkedRadio.value;\n }\n\n // For checkbox groups (multiple checkboxes with same name), upgrade to array schema\n if (control instanceof HTMLInputElement && control.type === 'checkbox') {\n const checkboxValues = collectCheckboxEnum(form, fieldKey);\n if (checkboxValues.length > 1) {\n const arrayProp: JsonSchemaProperty = {\n type: 'array',\n items: { type: 'string', enum: checkboxValues },\n title: schemaProp.title,\n };\n if (schemaProp.description) arrayProp.description = schemaProp.description;\n const checkedBoxes = Array.from(form.elements)\n .filter(\n (el): el is HTMLInputElement =>\n el instanceof HTMLInputElement &&\n el.type === 'checkbox' &&\n el.name === fieldKey &&\n el.checked,\n )\n .map((b) => b.value);\n if (checkedBoxes.length > 0) arrayProp.default = checkedBoxes;\n properties[fieldKey] = arrayProp;\n if (control.required) required.push(fieldKey);\n continue;\n }\n }\n\n properties[fieldKey] = schemaProp;\n\n // Track id-keyed or aria-label-keyed fields for the interceptor\n if (!name) {\n fieldElements.set(fieldKey, control);\n }\n\n // Mark as required \u2014 check both the native attribute AND shadow host chain.\n // Web Component wrappers (e.g. lightning-input-field[required]) set required on the\n // host element, not on the inner <input>, so control.required may be false even when\n // the field is logically required.\n let isRequired = control.required;\n if (!isRequired) {\n let hostNode: Element = control;\n while (true) {\n const root = hostNode.getRootNode();\n if (!(root instanceof ShadowRoot)) break;\n const host = root.host;\n if (host.hasAttribute('required') || host.getAttribute('aria-required') === 'true') {\n isRequired = true;\n break;\n }\n hostNode = host;\n }\n }\n if (isRequired) required.push(fieldKey);\n }\n\n // ARIA role-based controls (custom components not using native inputs)\n const ariaControls = collectAriaControls(form);\n const processedAriaRadioGroups = new Set<string>();\n\n for (const { el, role, key, enumValues, enumOneOf } of ariaControls) {\n if (properties[key]) continue; // already covered by a native control\n\n if (role === 'radio') {\n if (processedAriaRadioGroups.has(key)) continue;\n processedAriaRadioGroups.add(key);\n }\n\n const schemaProp = ariaRoleToSchema(el, role);\n\n // Apply pre-computed enum for grouped ARIA radiogroups (role=\"radiogroup\" ancestor)\n if (enumValues && enumValues.length > 0) {\n schemaProp.enum = enumValues;\n if (enumOneOf && enumOneOf.length > 0) schemaProp.oneOf = enumOneOf;\n }\n\n schemaProp.title = inferAriaFieldTitle(el);\n const desc = inferAriaFieldDescription(el);\n if (desc) schemaProp.description = desc;\n\n properties[key] = schemaProp;\n fieldElements.set(key, el);\n\n if (el.getAttribute('aria-required') === 'true') {\n required.push(key);\n }\n }\n\n return { schema: { '$schema': 'https://json-schema.org/draft/2020-12/schema', type: 'object', properties, required }, fieldElements };\n}\n\n/** Matches auto-generated IDs that frameworks (React, etc.) assign and that carry no semantic meaning. */\nconst AUTO_GENERATED_ID_RE = /^_r_[0-9a-z]+_$|^:[a-z0-9]+:$/i;\n\n/** Derive a schema key for a native control that lacks a name attribute */\nfunction resolveNativeControlFallbackKey(\n control: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement,\n): string | null {\n const el = control as HTMLElement;\n if (el.dataset['webmcpName']) return sanitizeName(el.dataset['webmcpName']!);\n // Skip auto-generated framework IDs (React: _r_1_, :r0:) \u2014 they are not meaningful.\n // Prefer aria-label or placeholder which carry semantic meaning.\n if (control.id && !AUTO_GENERATED_ID_RE.test(control.id)) return sanitizeName(control.id);\n const label = control.getAttribute('aria-label');\n if (label) return sanitizeName(label);\n // Fallback: placeholder text \u2014 common for minimalist forms (e.g. Ghost newsletter)\n // that have no name/id/aria-label on their inputs.\n if (\n (control instanceof HTMLInputElement || control instanceof HTMLTextAreaElement) &&\n control.placeholder?.trim()\n ) {\n return sanitizeName(control.placeholder.trim());\n }\n // Shadow host chain: Web Component wrappers (e.g. Salesforce lightning-input-field,\n // lightning-input) carry the semantic field identifier on the host element, not on the\n // inner <input>. Walk up through shadow roots to find field-name / label / name / aria-label.\n const hostKey = resolveShadowHostKey(control);\n if (hostKey) return hostKey;\n // Final fallback: input type for typed inputs without any text identifier.\n if (control instanceof HTMLInputElement && control.type !== 'text') {\n return control.type;\n }\n return null;\n}\n\n/**\n * Walk up the shadow root host chain from a shadow DOM element and return the first\n * meaningful field identifier found on a host element.\n * Checks (in order): field-name, label, aria-label, name attributes.\n * Covers Salesforce LWC (lightning-input-field[field-name]), generic Web Components,\n * and any design system that wraps native inputs in custom elements.\n */\nfunction resolveShadowHostKey(el: Element): string | null {\n let node: Element = el;\n while (true) {\n const root = node.getRootNode();\n if (!(root instanceof ShadowRoot)) break;\n const host = root.host;\n const fieldName = host.getAttribute('field-name');\n if (fieldName) { console.log('[auto-webmcp] shadow host key: field-name=', fieldName); return sanitizeName(fieldName); }\n const hostLabel = host.getAttribute('label') || host.getAttribute('aria-label');\n if (hostLabel) { console.log('[auto-webmcp] shadow host key: label=', hostLabel); return sanitizeName(hostLabel); }\n const hostName = host.getAttribute('name');\n if (hostName) { console.log('[auto-webmcp] shadow host key: name=', hostName); return sanitizeName(hostName); }\n node = host;\n }\n return null;\n}\n\n/** Key derivation for ARIA / contenteditable elements that are not native form controls. */\nfunction resolveAriaElementKey(el: Element): string | null {\n if ((el as HTMLElement).dataset['webmcpName']) return sanitizeName((el as HTMLElement).dataset['webmcpName']!);\n if (el.id && !AUTO_GENERATED_ID_RE.test(el.id)) return sanitizeName(el.id);\n const label = el.getAttribute('aria-label');\n if (label) return sanitizeName(label);\n const placeholder = el.getAttribute('placeholder');\n if (placeholder) return sanitizeName(placeholder);\n return null;\n}\n\ntype AriaControlEntry = {\n el: Element;\n role: AriaRole;\n key: string;\n enumValues?: string[];\n enumOneOf?: Array<{ const: string; title: string }>;\n};\n\n/** Collect ARIA-role-based interactive elements inside a form, excluding native inputs.\n * ARIA radio elements inside a role=\"radiogroup\" ancestor are collapsed into a single\n * enum field keyed on the group, matching native radio group behaviour. */\nfunction collectAriaControls(form: HTMLFormElement): Array<AriaControlEntry> {\n const selector = ARIA_ROLES_TO_SCAN.map((r) => `[role=\"${r}\"]`).join(', ');\n const rawResults: Array<{ el: Element; role: AriaRole; key: string }> = [];\n\n for (const el of Array.from(form.querySelectorAll(selector))) {\n // Skip native inputs \u2014 already handled above\n if (\n el instanceof HTMLInputElement ||\n el instanceof HTMLTextAreaElement ||\n el instanceof HTMLSelectElement\n ) continue;\n\n // Skip hidden elements\n if (el.getAttribute('aria-hidden') === 'true' || (el as HTMLElement).hidden) continue;\n\n const role = el.getAttribute('role') as AriaRole;\n const key = resolveAriaFieldKey(el);\n if (!key) continue;\n\n rawResults.push({ el, role, key });\n }\n\n // Group ARIA radios by their nearest role=\"radiogroup\" ancestor\n const radioEntries = rawResults.filter((e) => e.role === 'radio');\n const nonRadioEntries: AriaControlEntry[] = rawResults.filter((e) => e.role !== 'radio');\n\n const radioGroupMap = new Map<Element, Array<Element>>();\n const ungroupedRadios: AriaControlEntry[] = [];\n\n for (const entry of radioEntries) {\n const group = entry.el.closest('[role=\"radiogroup\"]');\n if (group) {\n if (!radioGroupMap.has(group)) radioGroupMap.set(group, []);\n radioGroupMap.get(group)!.push(entry.el);\n } else {\n ungroupedRadios.push(entry);\n }\n }\n\n const groupedEntries: AriaControlEntry[] = [];\n for (const [group, members] of radioGroupMap) {\n const groupKey = resolveAriaFieldKey(group);\n if (!groupKey) continue;\n const enumValues = members\n .map((el) => (el.getAttribute('data-value') ?? el.getAttribute('aria-label') ?? el.textContent ?? '').trim())\n .filter(Boolean);\n const enumOneOf = members\n .map((el) => {\n const val = (el.getAttribute('data-value') ?? el.getAttribute('aria-label') ?? el.textContent ?? '').trim();\n const title = (el.getAttribute('aria-label') ?? el.textContent ?? '').trim();\n return { const: val, title: title || val };\n })\n .filter((e) => e.const !== '');\n if (enumValues.length > 0) {\n groupedEntries.push({ el: group, role: 'radio', key: groupKey, enumValues, enumOneOf });\n }\n }\n\n return [...nonRadioEntries, ...groupedEntries, ...ungroupedRadios];\n}\n\n/** Derive a schema key from an ARIA element */\nfunction resolveAriaFieldKey(el: Element): string | null {\n const htmlEl = el as HTMLElement;\n if (htmlEl.dataset?.['webmcpName']) return sanitizeName(htmlEl.dataset['webmcpName']!);\n if (el.id) return sanitizeName(el.id);\n const label = el.getAttribute('aria-label');\n if (label) return sanitizeName(label);\n const labelledById = el.getAttribute('aria-labelledby');\n if (labelledById) {\n const text = document.getElementById(labelledById)?.textContent?.trim();\n if (text) return sanitizeName(text);\n }\n return null;\n}\n\nfunction inferAriaFieldTitle(el: Element): string {\n const htmlEl = el as HTMLElement;\n if (htmlEl.dataset?.['webmcpTitle']) return htmlEl.dataset['webmcpTitle']!;\n const label = el.getAttribute('aria-label');\n if (label) return label.trim();\n const labelledById = el.getAttribute('aria-labelledby');\n if (labelledById) {\n const text = document.getElementById(labelledById)?.textContent?.trim();\n if (text) return text;\n }\n if (el.id) return humanizeName(el.id);\n return '';\n}\n\nfunction inferAriaFieldDescription(el: Element): string {\n const nativeParamDesc = el.getAttribute('toolparamdescription');\n if (nativeParamDesc) return nativeParamDesc.trim();\n const htmlEl = el as HTMLElement;\n if (htmlEl.dataset?.['webmcpDescription']) return htmlEl.dataset['webmcpDescription']!;\n const ariaDesc = el.getAttribute('aria-description');\n if (ariaDesc) return ariaDesc;\n const describedById = el.getAttribute('aria-describedby');\n if (describedById) {\n const text = document.getElementById(describedById)?.textContent?.trim();\n if (text) return text;\n }\n const placeholder = el.getAttribute('placeholder') ?? (el as HTMLElement).dataset?.['placeholder'];\n if (placeholder) return placeholder.trim();\n return '';\n}\n\nfunction inferFieldTitle(\n control: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement,\n): string {\n // 1. data-webmcp-title\n if ('dataset' in control && (control as HTMLElement).dataset['webmcpTitle']) {\n return (control as HTMLElement).dataset['webmcpTitle']!;\n }\n\n // 2. Associated <label> text\n const labelText = getAssociatedLabelText(control);\n if (labelText) return labelText;\n\n // 3. name attribute (humanised)\n if (control.name) return humanizeName(control.name);\n\n // 4. id attribute (humanised)\n if (control.id) return humanizeName(control.id);\n\n // 5. placeholder text (last resort for inputs with no name/id/label)\n if (\n (control instanceof HTMLInputElement || control instanceof HTMLTextAreaElement) &&\n control.placeholder?.trim()\n ) {\n return control.placeholder.trim();\n }\n\n return '';\n}\n\nfunction inferFieldDescription(\n control: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement,\n): string {\n // 1. Native toolparamdescription attribute (spec)\n const nativeParamDesc = control.getAttribute('toolparamdescription');\n if (nativeParamDesc) return nativeParamDesc.trim();\n\n // 2. data-webmcp-description\n const el = control as HTMLElement;\n if (el.dataset['webmcpDescription']) return el.dataset['webmcpDescription']!;\n\n // 2. aria-description or aria-describedby\n const ariaDesc = control.getAttribute('aria-description');\n if (ariaDesc) return ariaDesc;\n\n const describedById = control.getAttribute('aria-describedby');\n if (describedById) {\n const descEl = document.getElementById(describedById);\n if (descEl?.textContent?.trim()) return descEl.textContent.trim();\n }\n\n // 3. placeholder (only as a last resort \u2014 can be noisy)\n if (control instanceof HTMLInputElement || control instanceof HTMLTextAreaElement) {\n const ph = control.placeholder?.trim();\n if (ph && ph.length > 0) return ph;\n }\n\n // 4. Associated label text (if title didn't use it, use it for description)\n return '';\n}\n\nfunction getAssociatedLabelText(\n control: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement,\n): string {\n // 1a. Labels collection (for/id association) \u2014 light DOM\n if (control.id) {\n const label = document.querySelector<HTMLLabelElement>(`label[for=\"${CSS.escape(control.id)}\"]`);\n if (label) {\n const text = labelTextWithoutNested(label);\n if (text) return text;\n }\n }\n\n // 1b. Same shadow root label search \u2014 Web Components render labels inside their own\n // shadow root so document.querySelector() cannot find them. Search the same root node.\n const ownRoot = control.getRootNode();\n if (ownRoot instanceof ShadowRoot) {\n if (control.id) {\n const shadowLabel = ownRoot.querySelector<HTMLLabelElement>(`label[for=\"${CSS.escape(control.id)}\"]`);\n if (shadowLabel) {\n const text = labelTextWithoutNested(shadowLabel);\n if (text) return text;\n }\n }\n // Also try: first <label> or element with slot=\"label\" in same shadow root\n const anyLabel = ownRoot.querySelector<HTMLLabelElement>('label');\n if (anyLabel) {\n const text = labelTextWithoutNested(anyLabel);\n if (text) return text;\n }\n }\n\n // 2. Wrapping <label> (works within the same shadow root via closest())\n const parent = control.closest('label');\n if (parent) {\n const text = labelTextWithoutNested(parent);\n if (text) return text;\n }\n\n // 3. label / aria-label on shadow host elements\n let node: Element = control;\n while (true) {\n const root = node.getRootNode();\n if (!(root instanceof ShadowRoot)) break;\n const host = root.host;\n const hostLabel = host.getAttribute('label') || host.getAttribute('aria-label');\n if (hostLabel) return hostLabel;\n node = host;\n }\n\n return '';\n}\n\nfunction labelTextWithoutNested(label: HTMLLabelElement): string {\n // Clone and remove any nested input/select/textarea before getting text\n const clone = label.cloneNode(true) as HTMLLabelElement;\n clone.querySelectorAll('input, select, textarea, button').forEach((el) => el.remove());\n return clone.textContent?.trim() ?? '';\n}\n\nfunction humanizeName(raw: string): string {\n return raw\n .replace(/[-_]/g, ' ')\n .replace(/([a-z])([A-Z])/g, '$1 $2')\n .trim()\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n}\n\n/**\n * Returns false if the control is currently not visible to the user:\n * display:none, visibility:hidden, aria-hidden ancestor, or inside a disabled fieldset.\n * Used to exclude conditional/hidden fields from the registered schema so agents\n * are not offered fields that are not currently applicable.\n *\n * Note: <input type=\"hidden\"> is excluded earlier by inputTypeToSchema returning null \u2014\n * this check never runs for hidden-type inputs.\n */\nfunction isControlVisible(el: HTMLElement): boolean {\n const style = window.getComputedStyle(el);\n if (style.display === 'none') return false;\n if (style.visibility === 'hidden') return false;\n // offsetParent is null for display:none (already caught) AND position:fixed (which IS visible)\n if (el.offsetParent === null && style.position !== 'fixed') return false;\n\n // Walk up ancestors checking for aria-hidden\n let node: Element | null = el;\n while (node && node !== document.body) {\n if (node.getAttribute('aria-hidden') === 'true') return false;\n node = node.parentElement;\n }\n\n // Disabled fieldset hides all its controls from agents\n if (el.closest('fieldset')?.disabled) return false;\n\n return true;\n}\n\n// ---------------------------------------------------------------------------\n// Orphan input group analysis (inputs not inside a <form> element)\n// ---------------------------------------------------------------------------\n\n/**\n * Derive ToolMetadata from a group of form controls that are NOT inside a <form>.\n * Used by discovery.ts's orphan-input scanner for pages like newsletter landing pages.\n */\nexport function analyzeOrphanInputGroup(\n container: Element,\n inputs: Array<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | HTMLElement>,\n submitBtn: HTMLButtonElement | HTMLInputElement | null,\n): ToolMetadata {\n const name = inferOrphanToolName(container, submitBtn);\n const description = inferOrphanToolDescription(container);\n const { schema: inputSchema, fieldElements } = buildSchemaFromInputs(inputs);\n const annotations = inferOrphanAnnotations(submitBtn);\n return { name, description, inputSchema, annotations, fieldElements };\n}\n\nfunction inferOrphanAnnotations(\n submitBtn: HTMLButtonElement | HTMLInputElement | null,\n): ToolAnnotations {\n const annotations: ToolAnnotations = {};\n const submitText =\n submitBtn instanceof HTMLInputElement\n ? submitBtn.value.trim()\n : (submitBtn?.textContent?.trim() ?? '');\n\n if (READONLY_BUTTON_PATTERNS.test(submitText)) {\n annotations.readOnlyHint = true;\n annotations.idempotentHint = true;\n }\n if (DESTRUCTIVE_BUTTON_PATTERNS.test(submitText)) {\n annotations.destructiveHint = true;\n }\n if (annotations.readOnlyHint !== true) {\n annotations.openWorldHint = true;\n }\n\n const hasNonDefault =\n annotations.readOnlyHint === true ||\n annotations.destructiveHint === true ||\n annotations.idempotentHint === true ||\n annotations.openWorldHint === false;\n\n return hasNonDefault ? annotations : {};\n}\n\nfunction inferOrphanToolName(\n container: Element,\n submitBtn: HTMLButtonElement | HTMLInputElement | null,\n): string {\n // 1. Submit button text\n if (submitBtn) {\n const text =\n submitBtn instanceof HTMLInputElement\n ? submitBtn.value.trim()\n : submitBtn.textContent?.trim() ?? '';\n if (text && text.length > 0 && text.length < 80) return sanitizeName(text);\n }\n\n // 2. Nearest heading within or above the container\n const heading = getNearestHeadingTextFrom(container);\n if (heading) return sanitizeName(heading);\n\n // 3. Page title\n const title = document.title?.trim();\n if (title) return sanitizeName(title);\n\n return `form_${++formIndex}`;\n}\n\nfunction inferOrphanToolDescription(container: Element): string {\n // Nearest heading within or above the container\n const heading = getNearestHeadingTextFrom(container);\n const pageTitle = document.title?.trim();\n if (heading && pageTitle && heading !== pageTitle) return `${heading} on ${pageTitle}`;\n if (heading) return heading;\n if (pageTitle) return pageTitle;\n return 'Submit form';\n}\n\n/**\n * Generic heading search starting from any Element (not just HTMLFormElement).\n * Walks up the DOM checking preceding siblings and parent elements.\n */\nfunction getNearestHeadingTextFrom(el: Element): string {\n // Also check headings inside the container itself\n const inner = el.querySelector('h1, h2, h3');\n if (inner?.textContent?.trim()) return inner.textContent.trim();\n\n let node: Element | null = el;\n while (node) {\n let sibling = node.previousElementSibling;\n while (sibling) {\n if (/^H[1-3]$/i.test(sibling.tagName)) {\n const text = sibling.textContent?.trim() ?? '';\n if (text) return text;\n }\n sibling = sibling.previousElementSibling;\n }\n node = node.parentElement;\n if (!node || node === document.body) break;\n }\n return '';\n}\n\n/**\n * Build a JSON Schema from an array of form controls (no <form> context needed).\n * Reuses the same field title/description inference as buildSchema().\n */\nfunction buildSchemaFromInputs(\n inputs: Array<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | HTMLElement>,\n): { schema: JsonSchema; fieldElements: Map<string, Element> } {\n const properties: Record<string, JsonSchemaProperty> = {};\n const required: string[] = [];\n const fieldElements = new Map<string, Element>();\n const processedRadioGroups = new Set<string>();\n const processedCheckboxGroups = new Set<string>();\n\n for (const control of inputs) {\n // ARIA / contenteditable elements (e.g. Twitter compose box, Notion editor)\n if (!(control instanceof HTMLInputElement) && !(control instanceof HTMLTextAreaElement) && !(control instanceof HTMLSelectElement)) {\n const fieldKey = resolveAriaElementKey(control);\n if (!fieldKey) continue;\n if (!isControlVisible(control)) continue;\n const prop: JsonSchemaProperty = { type: 'string' };\n prop.title = control.getAttribute('aria-label') ?? fieldKey;\n const desc = control.getAttribute('aria-description') ?? control.getAttribute('aria-describedby') ? null : null;\n if (desc) prop.description = desc;\n properties[fieldKey] = prop;\n fieldElements.set(fieldKey, control);\n required.push(fieldKey);\n continue;\n }\n\n const rawName = (control as HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement).name;\n // Sanitize the raw name (handles namespaced names like \"issue[title]\" \u2192 \"issue_title\")\n // so schema keys match the sanitized keys used in the execute handler.\n const fieldKey = (rawName ? sanitizeName(rawName) : null) || resolveNativeControlFallbackKey(control);\n if (!fieldKey) continue;\n\n if (control instanceof HTMLInputElement && control.type === 'radio') {\n if (processedRadioGroups.has(fieldKey)) continue;\n processedRadioGroups.add(fieldKey);\n }\n\n if (control instanceof HTMLInputElement && control.type === 'checkbox') {\n if (processedCheckboxGroups.has(fieldKey)) continue;\n processedCheckboxGroups.add(fieldKey);\n }\n\n const schemaProp = inputTypeToSchema(control);\n if (!schemaProp) continue; // skipped types (hidden, password, file, etc.)\n if (!isControlVisible(control)) continue; // display:none, aria-hidden, disabled fieldset\n\n schemaProp.title = inferFieldTitle(control);\n const desc = inferFieldDescription(control);\n if (desc) schemaProp.description = desc;\n\n // For checkbox groups, derive values from the inputs array (no form context here)\n if (control instanceof HTMLInputElement && control.type === 'checkbox') {\n const checkboxValues = inputs\n .filter((i): i is HTMLInputElement => i instanceof HTMLInputElement && i.type === 'checkbox' && i.name === fieldKey)\n .map((cb) => cb.value)\n .filter((v) => v !== '' && v !== 'on');\n if (checkboxValues.length > 1) {\n const arrayProp: JsonSchemaProperty = {\n type: 'array',\n items: { type: 'string', enum: checkboxValues },\n title: schemaProp.title,\n };\n if (schemaProp.description) arrayProp.description = schemaProp.description;\n properties[fieldKey] = arrayProp;\n if (control.required) required.push(fieldKey);\n continue;\n }\n }\n\n properties[fieldKey] = schemaProp;\n if (!rawName) fieldElements.set(fieldKey, control);\n if (control.required) required.push(fieldKey);\n }\n\n return { schema: { '$schema': 'https://json-schema.org/draft/2020-12/schema', type: 'object', properties, required }, fieldElements };\n}\n", "/**\n * registry.ts \u2014 Wrapper around navigator.modelContext WebMCP Imperative API\n */\n\nimport { ToolMetadata } from './analyzer.js';\n\n// ---------------------------------------------------------------------------\n// WebMCP type declarations (not yet in TypeScript DOM lib)\n// ---------------------------------------------------------------------------\n\nexport interface WebMCPTool {\n name: string;\n description: string;\n inputSchema: object;\n outputSchema?: object;\n annotations?: {\n readOnlyHint?: boolean;\n destructiveHint?: boolean;\n idempotentHint?: boolean;\n openWorldHint?: boolean;\n };\n execute: (params: Record<string, unknown>, client?: unknown) => Promise<unknown>;\n}\n\n/**\n * Describes the structured JSON object returned by every auto-webmcp execute handler.\n * Declared as outputSchema on tool registration so agents can parse results reliably.\n */\nconst EXECUTE_OUTPUT_SCHEMA: object = {\n type: 'object',\n properties: {\n status: {\n type: 'string',\n enum: ['success', 'partial', 'error', 'awaiting_user_action', 'timed_out', 'blocked_invalid'],\n description: 'Outcome of the form execution.',\n },\n filled_fields: {\n type: 'object',\n description: 'Field name to submitted value map.',\n },\n skipped_fields: {\n type: 'array',\n items: { type: 'string' },\n description: 'Fields the agent provided but that could not be filled.',\n },\n missing_required: {\n type: 'array',\n items: { type: 'string' },\n description: 'Required fields not supplied by the agent.',\n },\n validation_errors: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n field: { type: 'string' },\n constraint: { type: 'string', description: 'HTML ValidityState key that failed.' },\n message: { type: 'string' },\n },\n required: ['field', 'constraint', 'message'],\n },\n description: 'Per-field HTML5 validation failures (present when status is blocked_invalid).',\n },\n existing_values: {\n type: 'object',\n description: 'Field values present in the form before the agent filled it.',\n },\n warnings: {\n type: 'array',\n items: { type: 'object' },\n description: 'Non-fatal fill warnings (alias_resolved, clamped, not_filled, etc.).',\n },\n },\n required: ['status', 'filled_fields', 'skipped_fields', 'missing_required', 'warnings'],\n};\n\ninterface ModelContextRegisterOptions {\n signal?: AbortSignal;\n}\n\ndeclare global {\n interface Navigator {\n modelContext?: {\n registerTool(tool: WebMCPTool, options?: ModelContextRegisterOptions): Promise<void> | void;\n unregisterTool?(name: string): Promise<void> | void;\n };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Registry\n// ---------------------------------------------------------------------------\n\n/** Tracks registered tools: form element \u2192 tool name */\nconst registeredTools = new Map<HTMLFormElement, string>();\n/** Tracks abort controllers for registrations, enabling signal-based unregister */\nconst registrationControllers = new Map<HTMLFormElement, AbortController>();\n\n/** True if the browser supports navigator.modelContext */\nexport function isWebMCPSupported(): boolean {\n return typeof navigator !== 'undefined' && typeof navigator.modelContext !== 'undefined';\n}\n\n/**\n * Register a form as a WebMCP tool.\n * Silently no-ops if WebMCP is not supported.\n */\nexport async function registerFormTool(\n form: HTMLFormElement,\n metadata: ToolMetadata,\n execute: (params: Record<string, unknown>, client?: unknown) => Promise<unknown>,\n): Promise<void> {\n if (!isWebMCPSupported()) return;\n\n // Unregister any previously-registered tool for this same form element\n const existing = registeredTools.get(form);\n if (existing) {\n await unregisterFormTool(form);\n }\n\n const toolDef: WebMCPTool = {\n name: metadata.name,\n description: metadata.description,\n inputSchema: metadata.inputSchema,\n outputSchema: EXECUTE_OUTPUT_SCHEMA,\n execute,\n };\n if (metadata.annotations && Object.keys(metadata.annotations).length > 0) {\n toolDef.annotations = metadata.annotations;\n }\n\n const controller = new AbortController();\n registrationControllers.set(form, controller);\n\n try {\n await navigator.modelContext!.registerTool(toolDef, { signal: controller.signal });\n } catch {\n // Chrome may hold a stale registration from a previous page load.\n // Unregister by name and retry once.\n try {\n await navigator.modelContext!.unregisterTool?.(metadata.name);\n await navigator.modelContext!.registerTool(toolDef, { signal: controller.signal });\n } catch {\n // Give up Chrome registration \u2014 local handlers and form:registered still work.\n }\n }\n\n registeredTools.set(form, metadata.name);\n}\n\n/**\n * Unregister the WebMCP tool associated with a form element.\n * Silently no-ops if not registered or WebMCP not supported.\n */\nexport async function unregisterFormTool(form: HTMLFormElement): Promise<void> {\n if (!isWebMCPSupported()) return;\n\n const name = registeredTools.get(form);\n if (!name) return;\n\n const controller = registrationControllers.get(form);\n if (controller) {\n controller.abort();\n registrationControllers.delete(form);\n }\n\n try {\n await navigator.modelContext!.unregisterTool?.(name);\n } catch {\n // Tool may have already been removed \u2014 ignore\n }\n\n registeredTools.delete(form);\n}\n\n/** Get the registered tool name for a form, if any */\nexport function getRegisteredToolName(form: HTMLFormElement): string | undefined {\n return registeredTools.get(form);\n}\n\n/** Return a snapshot of all currently registered form\u2192name pairs */\nexport function getAllRegisteredTools(): Array<{ form: HTMLFormElement; name: string }> {\n return Array.from(registeredTools.entries()).map(([form, name]) => ({ form, name }));\n}\n\n/** Unregister all tools (e.g. on teardown) */\nexport async function unregisterAll(): Promise<void> {\n const entries = Array.from(registeredTools.entries());\n await Promise.all(entries.map(([form]) => unregisterFormTool(form)));\n}\n", "/**\n * interceptor.ts \u2014 Form submit interception for agent-invoked submissions\n *\n * WebMCP's `execute` callback receives form parameters and is expected to\n * return a result. This module bridges the gap: it fills form fields with\n * the agent-supplied values, submits the form, and resolves the execute\n * promise with a structured response.\n */\n\nimport { ResolvedConfig } from './config.js';\nimport type { ToolMetadata } from './analyzer.js';\n\n// ---------------------------------------------------------------------------\n// Extended SubmitEvent types (WebMCP additions)\n// ---------------------------------------------------------------------------\n\ndeclare global {\n interface SubmitEvent {\n /** True when the form was submitted by an AI agent via WebMCP */\n agentInvoked?: boolean;\n /** Call to return a structured result to the agent */\n respondWith?: (promise: Promise<unknown>) => void;\n }\n}\n\nexport interface ExecuteResult {\n content: Array<{ type: 'text'; text: string }>;\n}\n\nexport interface FillWarning {\n field: string;\n type:\n | 'clamped'\n | 'not_filled'\n | 'missing_required'\n | 'type_mismatch'\n | 'alias_resolved'\n | 'blocked_submit'\n | 'timeout';\n message: string;\n original?: unknown;\n actual?: unknown;\n}\n\nexport interface ValidationError {\n field: string;\n /** HTML ValidityState key: valueMissing, typeMismatch, patternMismatch, tooLong, tooShort, rangeUnderflow, rangeOverflow, stepMismatch, customError, badInput */\n constraint: string;\n message: string;\n}\n\nexport interface StructuredExecuteData {\n status:\n | 'success'\n | 'partial'\n | 'error'\n | 'awaiting_user_action'\n | 'timed_out'\n | 'blocked_invalid';\n filled_fields: Record<string, unknown>;\n skipped_fields: string[];\n missing_required: string[];\n warnings: FillWarning[];\n /** Structured per-field validation errors (populated on blocked_invalid status). */\n validation_errors?: ValidationError[];\n /** Field values captured from the form before the agent filled it. */\n existing_values?: Record<string, unknown>;\n}\n\ninterface ModelContextClientLike {\n requestUserInteraction?: <T>(callback: () => Promise<T>) => Promise<T>;\n}\n\ntype Resolver = (result: ExecuteResult) => void;\ntype Rejecter = (error: Error) => void;\n\n/** Per-form pending execute promises */\nconst pendingExecutions = new WeakMap<\n HTMLFormElement,\n { resolve: Resolver; reject: Rejecter; timeoutId?: ReturnType<typeof setTimeout> }\n>();\n\n/** Per-form last-used params (for serializing id-keyed fields not in FormData) */\nconst lastParams = new WeakMap<HTMLFormElement, Record<string, unknown>>();\n\n/** Per-form field element map (key \u2192 DOM element for non-name-addressable fields) */\nconst formFieldElements = new WeakMap<HTMLFormElement, Map<string, Element>>();\n\n/** Per-form required fields that the agent did not supply (populated before submit, consumed in interceptor) */\nconst pendingWarnings = new WeakMap<HTMLFormElement, string[]>();\n\n/** Per-form fill warnings (invalid/out-of-range values detected during field filling) */\nconst pendingFillWarnings = new WeakMap<HTMLFormElement, FillWarning[]>();\n\n/**\n * Per-form snapshot of field values captured immediately after fillFormFields()\n * completes. Used by serializeFormData() so that id-keyed and ARIA-keyed fields\n * are serialized from the snapshot rather than re-querying a potentially stale\n * DOM (e.g. after a React reconciliation cycle resets field values).\n */\nconst lastFilledSnapshot = new WeakMap<HTMLFormElement, Record<string, unknown>>();\n\n/**\n * Per-form snapshot of field values captured BEFORE the agent fills the form.\n * Returned in execute results so agents know what was already in the form.\n */\nconst preFillValues = new WeakMap<HTMLFormElement, Record<string, unknown>>();\n\n// React-compatible native prototype setters (retrieved once at module init).\n// Using these bypasses React's controlled-input tracking so onChange fires correctly.\nconst _inputValueSetter: ((v: string) => void) | undefined =\n Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')?.set;\nconst _textareaValueSetter: ((v: string) => void) | undefined =\n Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, 'value')?.set;\nconst _checkedSetter: ((v: boolean) => void) | undefined =\n Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'checked')?.set;\n\nfunction normalizeAliasKey(raw: string): string {\n return raw.toLowerCase().replace(/[^a-z0-9]+/g, '');\n}\n\nfunction addAlias(\n index: Map<string, Set<string>>,\n alias: string | null | undefined,\n schemaKey: string,\n): void {\n if (!alias) return;\n const normalized = normalizeAliasKey(alias);\n if (!normalized) return;\n if (!index.has(normalized)) index.set(normalized, new Set<string>());\n index.get(normalized)!.add(schemaKey);\n}\n\nfunction buildAliasIndex(\n form: HTMLFormElement,\n metadata: ToolMetadata | undefined,\n): Map<string, Set<string>> {\n const index = new Map<string, Set<string>>();\n const properties = metadata?.inputSchema?.properties ?? {};\n\n for (const [schemaKey, prop] of Object.entries(properties)) {\n addAlias(index, schemaKey, schemaKey);\n addAlias(index, schemaKey.replace(/_/g, ' '), schemaKey);\n addAlias(index, prop.title, schemaKey);\n\n const nativeEl = findNativeField(form, schemaKey);\n const mappedEl = metadata?.fieldElements?.get(schemaKey);\n const el = nativeEl ?? mappedEl ?? null;\n if (!el) continue;\n const htmlEl = el as HTMLElement;\n addAlias(index, htmlEl.getAttribute('id'), schemaKey);\n addAlias(index, htmlEl.getAttribute('name'), schemaKey);\n addAlias(index, htmlEl.getAttribute('aria-label'), schemaKey);\n addAlias(index, htmlEl.getAttribute('placeholder'), schemaKey);\n if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement) {\n for (const label of Array.from(el.labels ?? [])) {\n addAlias(index, label.textContent?.trim(), schemaKey);\n }\n }\n }\n return index;\n}\n\nfunction resolveParamsForSchema(\n form: HTMLFormElement,\n params: Record<string, unknown>,\n metadata: ToolMetadata | undefined,\n config: ResolvedConfig,\n): { resolved: Record<string, unknown>; warnings: FillWarning[] } {\n const resolved: Record<string, unknown> = {};\n const warnings: FillWarning[] = [];\n const properties = metadata?.inputSchema?.properties ?? {};\n const aliasEnabled = config.paramBinding.enableAliasResolution;\n\n for (const [key, value] of Object.entries(params)) {\n if (key in properties) resolved[key] = value;\n }\n if (!aliasEnabled) return { resolved, warnings };\n\n const aliasIndex = buildAliasIndex(form, metadata);\n for (const [rawKey, value] of Object.entries(params)) {\n if (rawKey in properties) continue;\n const candidates = aliasIndex.get(normalizeAliasKey(rawKey));\n if (!candidates || candidates.size !== 1) continue;\n const target = Array.from(candidates)[0];\n if (!target || target in resolved) continue;\n resolved[target] = value;\n warnings.push({\n field: target,\n type: 'alias_resolved',\n original: rawKey,\n message: `resolved \"${rawKey}\" to schema field \"${target}\"`,\n });\n }\n return { resolved, warnings };\n}\n\nfunction collectInvalidFieldWarnings(form: HTMLFormElement): FillWarning[] {\n const warnings: FillWarning[] = [];\n const controls = Array.from(form.elements).filter(\n (el): el is HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement =>\n el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement,\n );\n for (const control of controls) {\n if (!control.willValidate) continue;\n if (control.checkValidity()) continue;\n const field = control.name || control.id || control.getAttribute('aria-label') || 'unknown_field';\n warnings.push({\n field,\n type: 'blocked_submit',\n message: control.validationMessage || `field \"${field}\" failed validation`,\n });\n }\n return warnings;\n}\n\n/**\n * Capture all current form field values before the agent fills them.\n * Uses FormData for named controls and falls back to direct property reads\n * for elements not included in FormData (e.g. unchecked checkboxes).\n */\nfunction captureCurrentValues(form: HTMLFormElement): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n try {\n const data = new FormData(form);\n for (const [key, val] of data.entries()) {\n if (result[key] !== undefined) {\n const existing = result[key];\n result[key] = Array.isArray(existing) ? [...existing, val] : [existing, val];\n } else {\n result[key] = val;\n }\n }\n } catch {\n // FormData constructor can throw for detached forms \u2014 return what we have\n }\n return result;\n}\n\n/**\n * Collect structured validation errors for all invalid form controls.\n * Returns richer information than collectInvalidFieldWarnings by including\n * the specific HTML ValidityState constraint that failed.\n */\nfunction collectValidationErrors(form: HTMLFormElement): ValidationError[] {\n const errors: ValidationError[] = [];\n for (const control of Array.from(form.elements)) {\n if (\n !(control instanceof HTMLInputElement) &&\n !(control instanceof HTMLTextAreaElement) &&\n !(control instanceof HTMLSelectElement)\n ) continue;\n if (!control.willValidate || control.checkValidity()) continue;\n const field = control.name || control.id || control.getAttribute('aria-label') || 'unknown_field';\n const v = control.validity;\n const constraint = v.valueMissing ? 'valueMissing'\n : v.typeMismatch ? 'typeMismatch'\n : v.patternMismatch ? 'patternMismatch'\n : v.tooLong ? 'tooLong'\n : v.tooShort ? 'tooShort'\n : v.rangeUnderflow ? 'rangeUnderflow'\n : v.rangeOverflow ? 'rangeOverflow'\n : v.stepMismatch ? 'stepMismatch'\n : v.customError ? 'customError'\n : 'badInput';\n errors.push({ field, constraint, message: control.validationMessage || `field \"${field}\" failed validation` });\n }\n return errors;\n}\n\n/**\n * Build an `execute` function for a form tool.\n *\n * When the agent calls execute(params):\n * 1. Fills form fields with the supplied params\n * 2. Fires a submit event (or auto-submits if configured)\n * 3. Resolves with structured form data once submitted\n */\nexport function buildExecuteHandler(\n form: HTMLFormElement,\n config: ResolvedConfig,\n toolName: string,\n metadata?: ToolMetadata,\n): (params: Record<string, unknown>, client?: unknown) => Promise<ExecuteResult> {\n // Store field element map for this form\n if (metadata?.fieldElements) {\n formFieldElements.set(form, metadata.fieldElements);\n }\n\n // Attach submit/reset listeners once per form\n attachSubmitInterceptor(form, toolName);\n\n return async (params: Record<string, unknown>, client?: unknown): Promise<ExecuteResult> => {\n const modelContextClient = client as ModelContextClientLike | undefined;\n if (\n config.autoSubmit &&\n metadata?.annotations?.destructiveHint === true &&\n typeof modelContextClient?.requestUserInteraction === 'function'\n ) {\n const approved = await modelContextClient.requestUserInteraction(async () => {\n return new Promise<boolean>((resolve) => {\n const ok = window.confirm(`Agent requested a destructive action via \"${toolName}\". Continue?`);\n resolve(ok);\n });\n });\n if (!approved) {\n window.dispatchEvent(new CustomEvent('toolcancel', { detail: { toolName } }));\n return { content: [{ type: 'text', text: `Cancelled \"${toolName}\" by user.` }] };\n }\n }\n\n pendingFillWarnings.set(form, []);\n pendingWarnings.delete(form);\n\n // Snapshot existing field values before filling so agents can see prior state.\n const existingSnapshot = captureCurrentValues(form);\n preFillValues.set(form, existingSnapshot);\n\n const { resolved: resolvedParams, warnings: aliasWarnings } = resolveParamsForSchema(\n form,\n params,\n metadata,\n config,\n );\n if (aliasWarnings.length > 0) {\n pendingFillWarnings.set(form, [...(pendingFillWarnings.get(form) ?? []), ...aliasWarnings]);\n }\n\n // If preserveExisting is enabled, skip filling fields that already have a non-empty value.\n let paramsToFill = resolvedParams;\n if (config.preserveExisting) {\n const preserved: FillWarning[] = [];\n paramsToFill = Object.fromEntries(\n Object.entries(resolvedParams).filter(([key]) => {\n const current = existingSnapshot[key];\n const hasValue = current !== undefined && current !== '' && current !== null;\n if (hasValue) {\n preserved.push({\n field: key,\n type: 'not_filled',\n message: `field \"${key}\" already has a value and preserveExisting is enabled`,\n });\n }\n return !hasValue;\n }),\n );\n if (preserved.length > 0) {\n pendingFillWarnings.set(form, [...(pendingFillWarnings.get(form) ?? []), ...preserved]);\n }\n }\n\n fillFormFields(form, paramsToFill);\n\n // Compute missing required fields now so they are available when the\n // form submits, regardless of whether autoSubmit is enabled.\n const missingNow = getMissingRequired(metadata, resolvedParams);\n if (missingNow.length > 0) pendingWarnings.set(form, missingNow);\n\n // Dispatch toolactivated event per spec\n window.dispatchEvent(new CustomEvent('toolactivated', { detail: { toolName } }));\n\n return new Promise<ExecuteResult>((resolve, reject) => {\n const timeoutMs = config.execution.timeoutMs;\n const timeoutId = setTimeout(() => {\n const pending = pendingExecutions.get(form);\n if (!pending) return;\n pendingExecutions.delete(form);\n const timedOutState = (\n config.autoSubmit ||\n form.hasAttribute('toolautosubmit') ||\n form.dataset['webmcpAutosubmit'] !== undefined\n ) ? 'timed_out' : 'awaiting_user_action';\n const warn: FillWarning = {\n field: '__form__',\n type: 'timeout',\n message: timedOutState === 'timed_out'\n ? `tool execution timed out after ${timeoutMs}ms`\n : `waiting for user submit (timed out after ${timeoutMs}ms)`,\n };\n const _existingValsTimeout = preFillValues.get(form);\n const structured: StructuredExecuteData = {\n status: timedOutState,\n filled_fields: serializeFormData(form, lastParams.get(form), formFieldElements.get(form)),\n skipped_fields: [],\n missing_required: pendingWarnings.get(form) ?? [],\n warnings: [...(pendingFillWarnings.get(form) ?? []), warn],\n ...(_existingValsTimeout !== undefined && { existing_values: _existingValsTimeout }),\n };\n pendingWarnings.delete(form);\n pendingFillWarnings.delete(form);\n lastFilledSnapshot.delete(form);\n preFillValues.delete(form);\n resolve({\n content: [\n { type: 'text', text: warn.message },\n { type: 'text', text: JSON.stringify(structured) },\n ],\n });\n }, timeoutMs);\n pendingExecutions.set(form, { resolve, reject, timeoutId });\n\n if (\n config.autoSubmit ||\n form.hasAttribute('toolautosubmit') ||\n form.dataset['webmcpAutosubmit'] !== undefined\n ) {\n // Wait for the form DOM to stabilize before submitting. Frameworks like\n // React 18 batch state updates asynchronously after InputEvents \u2014 a fixed\n // delay is unreliable. waitForDomStable polls for 150 ms of silence (no\n // mutations) and caps at 800 ms so we never hang indefinitely.\n waitForDomStable(form).then(async () => {\n try {\n // Re-fill after framework has committed state updates.\n fillFormFields(form, resolvedParams);\n\n // Retry up to 2 times if the framework reset any filled values.\n for (let attempt = 0; attempt < 2; attempt++) {\n const reset = getResetFields(form, resolvedParams, formFieldElements.get(form));\n if (reset.length === 0) break;\n fillFormFields(form, resolvedParams);\n await waitForDomStable(form, 400, 100);\n }\n\n // If the stored form was remounted (React, Turbo etc.), find the live\n // form via the submit button so requestSubmit() reaches the real DOM.\n let submitForm: HTMLFormElement = form;\n if (!form.isConnected) {\n const liveBtn = document.querySelector<HTMLElement>(\n 'button[type=\"submit\"]:not([disabled]), input[type=\"submit\"]:not([disabled])',\n );\n const found = liveBtn?.closest('form') as HTMLFormElement | null;\n if (found) {\n submitForm = found;\n const pending = pendingExecutions.get(form);\n const nextPending = pending?.timeoutId\n ? { resolve, reject, timeoutId: pending.timeoutId }\n : { resolve, reject };\n pendingExecutions.set(submitForm, nextPending);\n attachSubmitInterceptor(submitForm, toolName);\n }\n }\n\n // If the form was remounted, transfer the pending warnings to the live form.\n if (submitForm !== form && pendingWarnings.has(form)) {\n pendingWarnings.set(submitForm, pendingWarnings.get(form)!);\n pendingWarnings.delete(form);\n }\n\n if (!submitForm.checkValidity()) {\n const pending = pendingExecutions.get(submitForm) ?? pendingExecutions.get(form);\n if (pending) {\n if (pending.timeoutId) clearTimeout(pending.timeoutId);\n pendingExecutions.delete(submitForm);\n pendingExecutions.delete(form);\n const warnings = [\n ...(pendingFillWarnings.get(submitForm) ?? pendingFillWarnings.get(form) ?? []),\n ...collectInvalidFieldWarnings(submitForm),\n ];\n pendingFillWarnings.delete(submitForm);\n pendingFillWarnings.delete(form);\n const _existingValsBlocked = preFillValues.get(form);\n const structured: StructuredExecuteData = {\n status: 'blocked_invalid',\n filled_fields: serializeFormData(submitForm, lastParams.get(submitForm) ?? lastParams.get(form), formFieldElements.get(submitForm) ?? formFieldElements.get(form)),\n skipped_fields: [],\n missing_required: pendingWarnings.get(submitForm) ?? pendingWarnings.get(form) ?? [],\n warnings,\n validation_errors: collectValidationErrors(submitForm),\n ...(_existingValsBlocked !== undefined && { existing_values: _existingValsBlocked }),\n };\n pendingWarnings.delete(submitForm);\n pendingWarnings.delete(form);\n lastFilledSnapshot.delete(submitForm);\n lastFilledSnapshot.delete(form);\n preFillValues.delete(form);\n resolve({\n content: [\n { type: 'text', text: 'Form submission blocked by native validation.' },\n { type: 'text', text: JSON.stringify(structured) },\n ],\n });\n }\n return;\n }\n\n submitForm.requestSubmit();\n } catch (err) {\n reject(err instanceof Error ? err : new Error(String(err)));\n }\n });\n }\n // Otherwise: the form stays filled; human clicks submit,\n // which fires the submit event interceptor below.\n });\n };\n}\n\nfunction attachSubmitInterceptor(form: HTMLFormElement, toolName: string): void {\n // Guard against attaching multiple times\n if ((form as unknown as Record<string, unknown>)['__awmcp_intercepted']) return;\n (form as unknown as Record<string, unknown>)['__awmcp_intercepted'] = true;\n\n form.addEventListener('submit', (e: SubmitEvent) => {\n const pending = pendingExecutions.get(form);\n if (!pending) return; // Normal human submission \u2014 do nothing\n\n // Agent-invoked path\n const { resolve } = pending;\n if (pending.timeoutId) clearTimeout(pending.timeoutId);\n pendingExecutions.delete(form);\n\n const formData = serializeFormData(form, lastParams.get(form), formFieldElements.get(form));\n const existingVals = preFillValues.get(form);\n lastFilledSnapshot.delete(form);\n preFillValues.delete(form);\n const missingRequired = pendingWarnings.get(form) ?? [];\n pendingWarnings.delete(form);\n const fillWarnings = pendingFillWarnings.get(form) ?? [];\n pendingFillWarnings.delete(form);\n\n const skippedFields = fillWarnings\n .filter((w) => w.type === 'not_filled')\n .map((w) => w.field);\n\n const structured: StructuredExecuteData = {\n status: missingRequired.length > 0 || skippedFields.length > 0 ? 'partial' : 'success',\n filled_fields: formData,\n skipped_fields: skippedFields,\n missing_required: missingRequired,\n warnings: [\n ...missingRequired.map((f): FillWarning => ({\n field: f,\n type: 'missing_required',\n message: `required field \"${f}\" was not provided`,\n })),\n ...fillWarnings,\n ],\n ...(existingVals !== undefined && { existing_values: existingVals }),\n };\n\n const allWarnMessages = [\n ...(missingRequired.length ? [`required fields were not filled: ${missingRequired.join(', ')}`] : []),\n ...fillWarnings.map((w) => w.message),\n ];\n const warningText = allWarnMessages.length ? ` Note: ${allWarnMessages.join('; ')}.` : '';\n const text = `Form submitted. Fields: ${JSON.stringify(formData)}${warningText}`;\n const result: ExecuteResult = {\n content: [\n { type: 'text', text },\n { type: 'text', text: JSON.stringify(structured) },\n ],\n };\n\n if (e.agentInvoked && typeof e.respondWith === 'function') {\n // Native WebMCP path: use respondWith to return to browser\n e.preventDefault();\n e.respondWith(Promise.resolve(result));\n }\n resolve(result);\n });\n\n // Dispatch toolcancel when form is reset\n form.addEventListener('reset', () => {\n lastFilledSnapshot.delete(form);\n preFillValues.delete(form);\n window.dispatchEvent(new CustomEvent('toolcancel', { detail: { toolName } }));\n });\n}\n\n// ---------------------------------------------------------------------------\n// Field filling\n// ---------------------------------------------------------------------------\n\nfunction setReactValue(el: HTMLInputElement | HTMLTextAreaElement, v: string): void {\n el.focus();\n // Select all existing text so the insert replaces it\n el.select?.();\n\n // execCommand('insertText') simulates real typing \u2014 triggers native browser\n // input events that every framework (React, Catalyst, Stimulus, Vue, etc.)\n // listens to. Most reliable cross-framework approach.\n // Guard: execCommand can return true but silently fail for inputs inside\n // shadow DOM (e.g. GitHub's Catalyst components), so verify the value landed.\n if (document.execCommand('insertText', false, v) && el.value === v) {\n return;\n }\n\n // Fallback: native prototype setter (bypasses React controlled-input cache)\n const setter = el instanceof HTMLTextAreaElement ? _textareaValueSetter : _inputValueSetter;\n if (setter) {\n setter.call(el, v);\n } else {\n el.value = v;\n }\n el.dispatchEvent(new InputEvent('input', { bubbles: true, cancelable: true, inputType: 'insertText', data: v }));\n el.dispatchEvent(new Event('change', { bubbles: true }));\n}\n\nfunction setReactChecked(el: HTMLInputElement, checked: boolean): void {\n if (_checkedSetter) {\n _checkedSetter.call(el, checked);\n } else {\n el.checked = checked;\n }\n el.dispatchEvent(new Event('change', { bubbles: true }));\n}\n\n/**\n * Recursively search shadow roots for a native form control matching selector.\n * GitHub's Catalyst and other custom-element frameworks render inputs inside\n * shadow DOM that form.querySelector() cannot pierce.\n */\nfunction findInShadowRoots(\n root: Document | ShadowRoot,\n selector: string,\n): HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | null {\n for (const host of Array.from(root.querySelectorAll('*'))) {\n const sr = (host as Element).shadowRoot;\n if (!sr) continue;\n const found = sr.querySelector<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>(selector);\n if (found) return found;\n const deeper = findInShadowRoots(sr, selector);\n if (deeper) return deeper;\n }\n return null;\n}\n\nfunction getAssociatedInputsByName(\n form: HTMLFormElement,\n type: 'checkbox' | 'radio',\n name: string,\n): HTMLInputElement[] {\n return Array.from(form.elements).filter(\n (el): el is HTMLInputElement =>\n el instanceof HTMLInputElement &&\n el.type === type &&\n el.name === name,\n );\n}\n\n/** Find a native form control by name or id, including inside shadow DOM. */\nfunction findNativeField(\n form: HTMLFormElement,\n key: string,\n): HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | null {\n // Includes out-of-tree controls linked with form=\"...\".\n const named = form.elements.namedItem(key);\n if (\n typeof named === 'object' &&\n named !== null &&\n (named instanceof HTMLInputElement || named instanceof HTMLTextAreaElement || named instanceof HTMLSelectElement)\n ) {\n return named;\n }\n if (named instanceof RadioNodeList) {\n const first = named[0];\n const firstObj = first as unknown;\n if (\n firstObj instanceof HTMLInputElement ||\n firstObj instanceof HTMLTextAreaElement ||\n firstObj instanceof HTMLSelectElement\n ) {\n return firstObj;\n }\n }\n\n const esc = CSS.escape(key);\n // Light DOM first (fast path)\n const light =\n form.querySelector<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>(`[name=\"${esc}\"]`) ??\n form.querySelector<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>(\n `input#${esc}, textarea#${esc}, select#${esc}`,\n );\n if (light) return light;\n // Shadow DOM fallback: search all shadow roots in the document\n return (\n findInShadowRoots(document, `[name=\"${esc}\"]`) ??\n findInShadowRoots(document, `input#${esc}, textarea#${esc}, select#${esc}`)\n );\n}\n\nfunction fillFormFields(form: HTMLFormElement, params: Record<string, unknown>): void {\n lastParams.set(form, params);\n const fieldEls = formFieldElements.get(form);\n const snapshot: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(params)) {\n const input = findNativeField(form, key);\n\n if (input) {\n if (input instanceof HTMLInputElement) {\n fillInput(input, form, key, value);\n if (input.type === 'checkbox') {\n if (Array.isArray(value)) {\n snapshot[key] = getAssociatedInputsByName(form, 'checkbox', key)\n .filter((b) => b.checked)\n .map((b) => b.value);\n } else {\n snapshot[key] = input.checked;\n }\n } else {\n snapshot[key] = input.value;\n }\n } else if (input instanceof HTMLTextAreaElement) {\n setReactValue(input, String(value ?? ''));\n snapshot[key] = input.value;\n } else if (input instanceof HTMLSelectElement) {\n fillSelectElement(input, value, form, key);\n snapshot[key] = input.multiple\n ? Array.from(input.options).filter((o) => o.selected).map((o) => o.value)\n : input.value;\n }\n continue;\n }\n\n // Fall back to id-keyed or ARIA-role element from the field map.\n const ariaEl = fieldEls?.get(key);\n if (ariaEl) {\n // If the stored element was remounted by a framework (React, Catalyst etc.),\n // find a fresh reference using its actual DOM id (which may differ from the\n // sanitized schema key \u2014 e.g. \"repository-name-input\" \u2192 key \"repository_name_input\").\n let effectiveEl: Element = ariaEl;\n if (!ariaEl.isConnected) {\n const elId = (ariaEl as HTMLElement).id;\n if (elId) {\n const fresh =\n document.getElementById(elId) ??\n findInShadowRoots(document, `#${CSS.escape(elId)}`);\n if (fresh) effectiveEl = fresh;\n }\n }\n if (effectiveEl instanceof HTMLInputElement) {\n fillInput(effectiveEl, form, key, value);\n snapshot[key] = effectiveEl.type === 'checkbox' ? effectiveEl.checked : effectiveEl.value;\n } else if (effectiveEl instanceof HTMLTextAreaElement) {\n setReactValue(effectiveEl, String(value ?? ''));\n snapshot[key] = effectiveEl.value;\n } else if (effectiveEl instanceof HTMLSelectElement) {\n fillSelectElement(effectiveEl, value, form, key);\n snapshot[key] = effectiveEl.multiple\n ? Array.from(effectiveEl.options).filter((o) => o.selected).map((o) => o.value)\n : effectiveEl.value;\n } else {\n fillAriaField(effectiveEl, value);\n snapshot[key] = value; // Use the raw value for ARIA elements (no reliable DOM readback)\n }\n }\n }\n\n lastFilledSnapshot.set(form, snapshot);\n\n // Expose fill warnings for external readers (extension, bridge).\n // The WeakMap is not accessible outside the IIFE closure, so we mirror the\n // current warnings onto window so the extension can read them after filling.\n (window as unknown as Record<string, unknown>)['__lastFillWarnings'] =\n pendingFillWarnings.get(form) ?? [];\n}\n\nfunction fillInput(\n input: HTMLInputElement,\n form: HTMLFormElement,\n key: string,\n value: unknown,\n): void {\n const type = input.type.toLowerCase();\n\n if (type === 'checkbox') {\n // Agent may pass an array for checkbox groups (multiple checkboxes with same name)\n if (Array.isArray(value)) {\n const allBoxes = getAssociatedInputsByName(form, 'checkbox', key);\n for (const box of allBoxes) {\n setReactChecked(box, (value as unknown[]).map(String).includes(box.value));\n }\n return;\n }\n setReactChecked(input, Boolean(value));\n return;\n }\n\n if (type === 'number' || type === 'range') {\n const raw = String(value ?? '');\n const num = Number(raw);\n if (raw === '' || isNaN(num)) {\n pendingFillWarnings.get(form)?.push({\n field: key,\n type: 'type_mismatch',\n message: `\"${key}\" expects a number, got: ${JSON.stringify(value)}`,\n original: value,\n });\n return;\n }\n const min = input.min !== '' ? parseFloat(input.min) : -Infinity;\n const max = input.max !== '' ? parseFloat(input.max) : Infinity;\n if (num < min || num > max) {\n const clamped = Math.min(Math.max(num, min), max);\n pendingFillWarnings.get(form)?.push({\n field: key,\n type: 'clamped',\n message: `\"${key}\" value ${num} is outside allowed range [${input.min || '?'}, ${input.max || '?'}], clamped to ${clamped}`,\n original: num,\n actual: clamped,\n });\n input.value = String(clamped);\n } else {\n input.value = String(num);\n }\n input.dispatchEvent(new InputEvent('input', { bubbles: true, cancelable: true, inputType: 'insertText', data: String(num) }));\n input.dispatchEvent(new Event('change', { bubbles: true }));\n return;\n }\n\n if (type === 'radio') {\n const radios = getAssociatedInputsByName(form, 'radio', key);\n for (const radio of radios) {\n if (radio.value === String(value)) {\n if (_checkedSetter) {\n _checkedSetter.call(radio, true);\n } else {\n radio.checked = true;\n }\n radio.dispatchEvent(new Event('change', { bubbles: true }));\n break;\n }\n }\n return;\n }\n\n setReactValue(input, String(value ?? ''));\n}\n\n/**\n * Fill a select element. For multi-select, deselects all options then selects\n * those whose value is in the agent-supplied array. A single string value is\n * treated as a one-element array for multi-select. For single-select, tries\n * exact value match first, then case-insensitive label match as fallback.\n */\nfunction fillSelectElement(\n select: HTMLSelectElement,\n value: unknown,\n form?: HTMLFormElement,\n key?: string,\n): void {\n if (select.multiple) {\n const vals: string[] = Array.isArray(value)\n ? (value as unknown[]).map(String)\n : [String(value ?? '')];\n for (const opt of Array.from(select.options)) {\n opt.selected = vals.includes(opt.value);\n }\n select.dispatchEvent(new Event('change', { bubbles: true }));\n return;\n }\n\n const strVal = String(value ?? '');\n select.value = strVal;\n\n // If the value didn't match any option (browser silently ignores unknown values),\n // try case-insensitive label/text matching as a fallback.\n if (select.value !== strVal) {\n const lower = strVal.toLowerCase();\n const byLabel = Array.from(select.options).find(\n (o) => o.text.trim().toLowerCase() === lower || o.label.trim().toLowerCase() === lower,\n );\n if (byLabel) {\n select.value = byLabel.value;\n } else if (form && key) {\n // Neither value nor label matched \u2014 record a not_filled warning.\n pendingFillWarnings.get(form)?.push({\n field: key,\n type: 'not_filled',\n message: `\"${key}\" value \"${strVal}\" did not match any option in the select`,\n original: strVal,\n });\n }\n }\n\n select.dispatchEvent(new Event('change', { bubbles: true }));\n}\n\nfunction fillAriaField(el: Element, value: unknown): void {\n const role = el.getAttribute('role');\n\n if (role === 'checkbox' || role === 'switch') {\n el.setAttribute('aria-checked', String(Boolean(value)));\n el.dispatchEvent(new MouseEvent('click', { bubbles: true }));\n return;\n }\n\n if (role === 'radio') {\n el.setAttribute('aria-checked', 'true');\n el.dispatchEvent(new MouseEvent('click', { bubbles: true }));\n return;\n }\n\n if (role === 'radiogroup') {\n // Find the matching radio option inside the group and activate it\n const radios = Array.from(el.querySelectorAll('[role=\"radio\"]'));\n for (const radio of radios) {\n const val = (radio.getAttribute('data-value') ?? radio.getAttribute('aria-label') ?? radio.textContent ?? '').trim();\n if (val === String(value)) {\n radio.setAttribute('aria-checked', 'true');\n radio.dispatchEvent(new MouseEvent('click', { bubbles: true }));\n for (const other of radios) {\n if (other !== radio) other.setAttribute('aria-checked', 'false');\n }\n break;\n }\n }\n return;\n }\n\n // textbox, combobox, searchbox, spinbutton\n const htmlEl = el as HTMLElement;\n console.log('[auto-webmcp] fillAriaField', {\n tag: el.tagName, role, isContentEditable: htmlEl.isContentEditable,\n id: el.id, ariaLabel: el.getAttribute('aria-label'),\n textContentBefore: (htmlEl.textContent ?? '').slice(0, 80),\n });\n\n if (htmlEl.isContentEditable) {\n htmlEl.focus();\n\n // Select all content scoped to this element (not document.execCommand('selectAll')\n // which can confuse Quill/ProseMirror by triggering their select-all handling).\n const range = document.createRange();\n range.selectNodeContents(htmlEl);\n const sel = window.getSelection();\n sel?.removeAllRanges();\n sel?.addRange(range);\n\n const text = String(value ?? '');\n console.log('[auto-webmcp] fillAriaField: text to insert:', JSON.stringify(text));\n\n // Strategy 1: paste simulation (Draft.js preferred path).\n // Draft.js intercepts paste, reads clipboardData, updates EditorState, and\n // enables submit buttons. We check whether text actually landed in the DOM\n // after the event \u2014 if the editor handled paste but clipboardData was empty\n // (a Chrome security edge-case for synthetic events) we fall through.\n // NOTE: use .trim() \u2014 an empty Quill/ProseMirror editor may contain '<p><br></p>'\n // whose textContent is '\\n' (length 1), which would falsely indicate success.\n let inserted = false;\n try {\n const dt = new DataTransfer();\n dt.setData('text/plain', text);\n htmlEl.dispatchEvent(new ClipboardEvent('paste', {\n bubbles: true, cancelable: true, composed: true, clipboardData: dt,\n }));\n inserted = (htmlEl.textContent ?? '').trim().length > 0;\n console.log('[auto-webmcp] fillAriaField: S1 paste result:', inserted, JSON.stringify((htmlEl.textContent ?? '').slice(0, 80)));\n } catch (e) {\n console.log('[auto-webmcp] fillAriaField: S1 paste threw:', e);\n }\n\n if (!inserted) {\n // Strategy 2: execCommand insertText with the range selection active.\n // The browser replaces the selected content with the new text and fires\n // the native beforeinput + input events that Quill/ProseMirror listen to.\n const ok = document.execCommand('insertText', false, text);\n inserted = (htmlEl.textContent ?? '').trim().length > 0;\n console.log('[auto-webmcp] fillAriaField: S2 execCommand result:', ok, 'inserted:', inserted, JSON.stringify((htmlEl.textContent ?? '').slice(0, 80)));\n }\n\n if (!inserted) {\n // Strategy 3: direct beforeinput InputEvent (ProseMirror / LinkedIn editor).\n // Dispatching directly guarantees event.data === text; execCommand may fire\n // beforeinput with data=null in some Chrome versions.\n try {\n htmlEl.dispatchEvent(new InputEvent('beforeinput', {\n bubbles: true, cancelable: true, composed: true,\n inputType: 'insertText', data: text,\n }));\n inserted = (htmlEl.textContent ?? '').trim().length > 0;\n console.log('[auto-webmcp] fillAriaField: S3 beforeinput result:', inserted, JSON.stringify((htmlEl.textContent ?? '').slice(0, 80)));\n } catch (e) {\n console.log('[auto-webmcp] fillAriaField: S3 beforeinput threw:', e);\n }\n }\n\n if (!inserted) {\n // Strategy 4: direct textContent assignment (MutationObserver-based editors).\n // Sets content directly in the DOM; editors using MutationObserver (Quill,\n // ProseMirror) will pick it up and sync their internal model.\n htmlEl.textContent = text;\n const r2 = document.createRange();\n r2.selectNodeContents(htmlEl);\n r2.collapse(false);\n sel?.removeAllRanges();\n sel?.addRange(r2);\n console.log('[auto-webmcp] fillAriaField: S4 textContent assignment done, textContent:', JSON.stringify((htmlEl.textContent ?? '').slice(0, 80)));\n }\n\n // Always dispatch input so any remaining framework listeners are notified.\n htmlEl.dispatchEvent(new InputEvent('input', {\n bubbles: true, cancelable: true, inputType: 'insertText', data: text,\n }));\n console.log('[auto-webmcp] fillAriaField: done, final textContent:', JSON.stringify((htmlEl.textContent ?? '').slice(0, 80)));\n } else {\n console.log('[auto-webmcp] fillAriaField: not contentEditable, dispatching input/change only');\n el.dispatchEvent(new Event('input', { bubbles: true }));\n el.dispatchEvent(new Event('change', { bubbles: true }));\n }\n}\n\n// ---------------------------------------------------------------------------\n// Serialization\n// ---------------------------------------------------------------------------\n\nfunction serializeFormData(\n form: HTMLFormElement,\n params?: Record<string, unknown>,\n fieldEls?: Map<string, Element>,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n const data = new FormData(form);\n const snapshot = lastFilledSnapshot.get(form);\n\n for (const [key, val] of data.entries()) {\n if (result[key] !== undefined) {\n // Multiple values \u2192 array\n const existing = result[key];\n if (Array.isArray(existing)) {\n existing.push(val);\n } else {\n result[key] = [existing, val];\n }\n } else {\n result[key] = val;\n }\n }\n\n // Supplement with id-keyed or ARIA-keyed fields not captured by FormData.\n // Prefer the post-fill snapshot over a live DOM query: the DOM may have been\n // reset by a framework re-render (React reconciliation) between fill and submit.\n if (params) {\n for (const key of Object.keys(params)) {\n if (key in result) continue;\n\n if (snapshot && key in snapshot) {\n result[key] = snapshot[key];\n continue;\n }\n\n const el =\n findNativeField(form, key) ??\n fieldEls?.get(key) ??\n null;\n if (!el) continue;\n if (el instanceof HTMLInputElement && el.type === 'checkbox') {\n result[key] = el.checked;\n } else if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement) {\n result[key] = el.value;\n } else {\n // ARIA element\n const role = el.getAttribute('role');\n if (role === 'checkbox' || role === 'switch') {\n result[key] = el.getAttribute('aria-checked') === 'true';\n } else {\n result[key] = (el as HTMLElement).textContent?.trim() ?? '';\n }\n }\n }\n }\n\n return result;\n}\n\n/**\n * Fill a single form control or ARIA element with the given value.\n * Exported for use by orphan-input (formless) tool handlers in discovery.ts.\n */\n/**\n * Convert ISO date strings (YYYY-MM-DD) to MM/DD/YYYY for text inputs that\n * do not use type=\"date\". Browsers handle ISO natively for type=\"date\"; for\n * type=\"text\" date fields (Salesforce Lightning, many CRMs), the expected\n * format is typically MM/DD/YYYY. Only converts when the field name or id\n * contains \"date\" (case-insensitive) to avoid false positives.\n */\nfunction maybeConvertIsoDate(value: string, el: HTMLElement): string {\n const isoMatch = value.match(/^(\\d{4})-(\\d{2})-(\\d{2})$/);\n if (!isoMatch) return value;\n if (el instanceof HTMLInputElement && el.type === 'date') return value;\n const fieldHint = ((el as HTMLInputElement).name ?? el.id ?? '').toLowerCase();\n if (!/date/.test(fieldHint)) return value;\n const [, year, month, day] = isoMatch;\n return `${month}/${day}/${year}`;\n}\n\nexport function fillElement(\n el: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | Element,\n value: unknown,\n): void {\n if (el instanceof HTMLInputElement) {\n const type = el.type.toLowerCase();\n if (type === 'checkbox') {\n setReactChecked(el, Boolean(value));\n } else if (type === 'radio') {\n if (el.value === String(value)) {\n if (_checkedSetter) _checkedSetter.call(el, true);\n else el.checked = true;\n el.dispatchEvent(new Event('change', { bubbles: true }));\n }\n } else {\n setReactValue(el, maybeConvertIsoDate(String(value ?? ''), el));\n }\n } else if (el instanceof HTMLTextAreaElement) {\n setReactValue(el, String(value ?? ''));\n } else if (el instanceof HTMLSelectElement) {\n fillSelectElement(el, value);\n } else {\n fillAriaField(el, value);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers for DOM stabilization, reset detection, and required-field warnings\n// ---------------------------------------------------------------------------\n\n/**\n * Resolves when the form DOM has been stable (no mutations) for debounceMs,\n * or when maxMs has elapsed. Replaces the hardcoded 300 ms pre-submit delay\n * so React/Vue/etc. can finish committing batched state updates.\n */\nfunction waitForDomStable(\n form: HTMLFormElement,\n maxMs = 800,\n debounceMs = 150,\n): Promise<void> {\n return new Promise<void>((resolve) => {\n let settled = false;\n let debounceTimer: ReturnType<typeof setTimeout> | null = null;\n\n const settle = (): void => {\n if (settled) return;\n settled = true;\n observer.disconnect();\n if (debounceTimer !== null) clearTimeout(debounceTimer);\n resolve();\n };\n\n const observer = new MutationObserver(() => {\n if (debounceTimer !== null) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(settle, debounceMs);\n });\n\n observer.observe(form, { childList: true, subtree: true, attributes: true, characterData: true });\n\n setTimeout(settle, maxMs); // hard cap\n debounceTimer = setTimeout(settle, debounceMs); // resolves early if already stable\n });\n}\n\n/**\n * Returns the keys of params whose corresponding DOM field values no longer\n * match what was filled, indicating the framework reset them.\n */\nfunction getResetFields(\n form: HTMLFormElement,\n params: Record<string, unknown>,\n fieldEls: Map<string, Element> | undefined,\n): string[] {\n const reset: string[] = [];\n for (const [key, expected] of Object.entries(params)) {\n const el = findNativeField(form, key) ?? (fieldEls?.get(key) ?? null);\n if (!el) continue;\n if (el instanceof HTMLInputElement && el.type === 'checkbox') {\n if (el.checked !== Boolean(expected)) reset.push(key);\n } else if (\n el instanceof HTMLInputElement ||\n el instanceof HTMLTextAreaElement ||\n el instanceof HTMLSelectElement\n ) {\n if (el.value !== String(expected ?? '')) reset.push(key);\n }\n }\n return reset;\n}\n\n/**\n * Returns required field keys from the schema that the agent did not supply.\n * Uses the `in` operator so fields explicitly passed as empty string or false\n * are not flagged (intentional empty values are valid).\n */\nfunction getMissingRequired(\n metadata: ToolMetadata | undefined,\n params: Record<string, unknown>,\n): string[] {\n if (!metadata?.inputSchema?.required?.length) return [];\n return metadata.inputSchema.required.filter((fieldKey) => !(fieldKey in params));\n}\n\n/**\n * Async fill for `<button role=\"combobox\">` elements (Salesforce Lightning,\n * Atlaskit, and other JS-powered dropdowns). The pattern:\n * 1. Click the button to open the dropdown.\n * 2. Wait for a [role=\"listbox\"] to appear (up to 1s).\n * 3. Click the option whose data-value, aria-label, or text matches `value`.\n *\n * Exported for use by the orphan execute handler in discovery.ts.\n */\n/**\n * Find all elements with a given selector inside all shadow roots reachable from root.\n * Used by fillComboboxButton to locate listbox/option elements that Salesforce Lightning\n * renders inside overlay shadow roots (lightning-overlay-container etc.).\n */\nfunction queryShadowAll(root: Element | ShadowRoot | Document, selector: string): Element[] {\n const results: Element[] = [];\n const hosts = Array.from((root as Element | ShadowRoot).querySelectorAll?.('*') ?? []);\n for (const host of hosts) {\n const sr = (host as Element).shadowRoot;\n if (!sr) continue;\n results.push(...Array.from(sr.querySelectorAll(selector)));\n results.push(...queryShadowAll(sr, selector));\n }\n return results;\n}\n\nexport async function fillComboboxButton(el: Element, value: unknown): Promise<void> {\n const text = String(value ?? '').trim();\n console.log('[auto-webmcp] fillComboboxButton: clicking button, value=', JSON.stringify(text));\n\n // Fire pointerdown + click. LWC event handlers often listen to pointerdown to\n // manage focus/blur before click fires, so synthetic click alone is insufficient.\n el.dispatchEvent(new PointerEvent('pointerdown', { bubbles: true, cancelable: true }));\n el.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, cancelable: true }));\n el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));\n\n // Wait for a listbox to appear. Salesforce Lightning may render the dropdown in:\n // 1. The main document (portal pattern \u2014 found by document.querySelector)\n // 2. A shadow root of an overlay container (lightning-overlay-container etc.)\n // 3. The same shadow root as the button, referenced via aria-controls\n const ariaControlsId = el.getAttribute('aria-controls');\n\n const listbox = await new Promise<Element | null>((resolve) => {\n const deadline = Date.now() + 3000;\n const poll = (): void => {\n // Priority 1: button's aria-controls points directly to the listbox element.\n if (ariaControlsId) {\n const byId = document.getElementById(ariaControlsId);\n if (byId) { resolve(byId); return; }\n // aria-controls target may be inside a shadow root (Salesforce overlay service).\n const inShadow = queryShadowAll(document.body, `#${CSS.escape(ariaControlsId)}`)[0] ?? null;\n if (inShadow) { resolve(inShadow); return; }\n }\n // Priority 2: any visible [role=\"listbox\"] in the main document.\n const lightCandidate =\n document.querySelector('[role=\"listbox\"]') ??\n document.querySelector('[role=\"option\"]')?.closest('[role=\"listbox\"]') ??\n null;\n if (lightCandidate) { resolve(lightCandidate); return; }\n // Priority 3: [role=\"listbox\"] inside any shadow root (overlay service pattern).\n const shadowCandidate = queryShadowAll(document.body, '[role=\"listbox\"]')[0] ?? null;\n if (shadowCandidate) { resolve(shadowCandidate); return; }\n\n if (Date.now() >= deadline) { resolve(null); return; }\n setTimeout(poll, 50);\n };\n poll();\n });\n\n if (!listbox) {\n console.warn('[auto-webmcp] fillComboboxButton: listbox did not appear after 3s');\n return;\n }\n\n // Collect options from the listbox, including those inside its shadow DOM.\n const lightOptions = Array.from(listbox.querySelectorAll('[role=\"option\"]'));\n const shadowOptions = queryShadowAll(listbox, '[role=\"option\"]');\n const options = lightOptions.length > 0 ? lightOptions : shadowOptions;\n console.log('[auto-webmcp] fillComboboxButton: listbox has', options.length, 'option(s)');\n\n const lowerValue = text.toLowerCase();\n const match = options.find((opt) => {\n const dataValue = (opt.getAttribute('data-value') ?? '').toLowerCase();\n const ariaLabel = (opt.getAttribute('aria-label') ?? '').toLowerCase();\n const optText = (opt.textContent ?? '').trim().toLowerCase();\n return dataValue === lowerValue || ariaLabel === lowerValue || optText === lowerValue;\n });\n\n if (match) {\n console.log('[auto-webmcp] fillComboboxButton: selecting option', match.textContent?.trim());\n // Fire the same sequence on the option for LWC: pointerdown + mousedown + click.\n match.dispatchEvent(new PointerEvent('pointerdown', { bubbles: true, cancelable: true }));\n match.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, cancelable: true }));\n match.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));\n } else {\n console.warn('[auto-webmcp] fillComboboxButton: no option matched', JSON.stringify(text),\n 'available:', options.map((o) => (o.getAttribute('data-value') ?? o.textContent?.trim())));\n }\n}\n", "/**\n * discovery.ts \u2014 Form scanning & MutationObserver for SPA support\n */\n\nimport { ResolvedConfig } from './config.js';\nimport { analyzeForm, analyzeOrphanInputGroup, ToolAnnotations } from './analyzer.js';\nimport {\n registerFormTool,\n unregisterFormTool,\n isWebMCPSupported,\n getAllRegisteredTools,\n getRegisteredToolName,\n} from './registry.js';\nimport { buildExecuteHandler, fillElement, fillComboboxButton } from './interceptor.js';\nimport { ARIA_ROLES_TO_SCAN, JsonSchema } from './schema.js';\n\n// ---------------------------------------------------------------------------\n// Events\n// ---------------------------------------------------------------------------\n\nexport type FormLifecycleEvent = CustomEvent<{\n form: HTMLFormElement;\n toolName: string;\n}>;\n\nfunction emit(type: 'form:registered' | 'form:unregistered', form: HTMLFormElement, toolName: string): void {\n window.dispatchEvent(\n new CustomEvent(type, { detail: { form, toolName } }) as FormLifecycleEvent,\n );\n}\n\n// ---------------------------------------------------------------------------\n// Registration helpers\n// ---------------------------------------------------------------------------\n\n/** Check whether a form should be excluded per config */\nfunction isExcluded(form: HTMLFormElement, config: ResolvedConfig): boolean {\n // Skip forms with data-no-webmcp\n if (form.dataset['noWebmcp'] !== undefined) return true;\n // Skip per config exclude list\n for (const selector of config.exclude) {\n try {\n if (form.matches(selector)) return true;\n } catch {\n // invalid selector \u2014 ignore\n }\n }\n return false;\n}\n\nfunction withNumericSuffix(baseName: string, n: number): string {\n const suffix = `_${n}`;\n return `${baseName.slice(0, Math.max(1, 64 - suffix.length))}${suffix}`;\n}\n\nfunction getUsedToolNames(excludeForm?: HTMLFormElement): Set<string> {\n const names = new Set<string>(registeredOrphanToolNames);\n for (const { form, name } of getAllRegisteredTools()) {\n if (excludeForm && form === excludeForm) continue;\n names.add(name);\n }\n return names;\n}\n\nfunction ensureUniqueToolName(baseName: string, excludeForm?: HTMLFormElement): string {\n const used = getUsedToolNames(excludeForm);\n if (!used.has(baseName)) return baseName;\n let i = 2;\n let candidate = withNumericSuffix(baseName, i);\n while (used.has(candidate)) {\n i++;\n candidate = withNumericSuffix(baseName, i);\n }\n return candidate;\n}\n\nfunction hasNativeDeclarativeTool(form: HTMLFormElement): boolean {\n return form.getAttribute('toolname')?.trim().length ? true : false;\n}\n\nasync function registerForm(form: HTMLFormElement, config: ResolvedConfig): Promise<void> {\n if (isExcluded(form, config)) return;\n const previousName = getRegisteredToolName(form);\n\n if (hasNativeDeclarativeTool(form) && config.declarativeMode !== 'force') {\n if (previousName) {\n await unregisterFormTool(form);\n }\n if (config.debug) {\n const mode = config.declarativeMode;\n console.log(`[auto-webmcp] Skipping imperative registration for native declarative form (mode=${mode})`);\n }\n return;\n }\n\n // Find matching override (first matching selector wins)\n let override;\n for (const [selector, ovr] of Object.entries(config.overrides)) {\n try {\n if (form.matches(selector)) {\n override = ovr;\n break;\n }\n } catch {\n // invalid selector\n }\n }\n\n const metadata = analyzeForm(form, override);\n const resolvedName = ensureUniqueToolName(metadata.name, form);\n if (resolvedName !== metadata.name && config.debug) {\n console.warn(`[auto-webmcp] tool name collision: \"${metadata.name}\" renamed to \"${resolvedName}\"`);\n }\n metadata.name = resolvedName;\n\n if (config.debug) {\n warnToolQuality(metadata.name, metadata.description);\n }\n\n const execute = buildExecuteHandler(form, config, metadata.name, metadata);\n\n await registerFormTool(form, metadata, execute);\n registeredForms.add(form);\n registeredFormCount++;\n\n // Expose the submit button reference so background.js can click it via\n // direct DOM reference instead of relying on CSS selector matching.\n const formSubmitBtn = form.querySelector<HTMLButtonElement | HTMLInputElement>(\n '[type=\"submit\"], button[data-variant=\"primary\"], button:not([type])',\n ) ?? null;\n const pendingBtns = ((window as unknown as Record<string, unknown>)['__pendingSubmitBtns'] ??= {}) as Record<string, Element | null>;\n if (previousName && previousName !== metadata.name) {\n delete pendingBtns[previousName];\n }\n pendingBtns[metadata.name] = formSubmitBtn;\n\n if (config.debug) {\n console.log(`[auto-webmcp] Registered: ${metadata.name}`, metadata);\n }\n\n emit('form:registered', form, metadata.name);\n}\n\nasync function unregisterForm(form: HTMLFormElement, config: ResolvedConfig): Promise<void> {\n const name = getRegisteredToolName(form);\n if (!name) return;\n\n await unregisterFormTool(form);\n registeredForms.delete(form);\n const pendingBtns = (window as unknown as Record<string, unknown>)['__pendingSubmitBtns'] as Record<string, Element | null> | undefined;\n if (pendingBtns) delete pendingBtns[name];\n\n if (config.debug) {\n console.log(`[auto-webmcp] Unregistered: ${name}`);\n }\n\n emit('form:unregistered', form, name);\n}\n\n// ---------------------------------------------------------------------------\n// MutationObserver\n// ---------------------------------------------------------------------------\n\nlet observer: MutationObserver | null = null;\n\n/** Set of currently registered forms, used to detect lazy-rendered child inputs. */\nconst registeredForms = new WeakSet<HTMLFormElement>();\n\n/** Count of forms registered in the current discovery session. Reset on each startDiscovery call. */\nlet registeredFormCount = 0;\n\n/** Debounce timers for re-analysis when inputs are added to existing forms. */\nconst reAnalysisTimers = new Map<HTMLFormElement, ReturnType<typeof setTimeout>>();\nconst RE_ANALYSIS_DEBOUNCE_MS = 300;\n\n/**\n * Debounce timer for re-running scanOrphanInputs when the DOM gains new\n * orphan-eligible elements (e.g. Salesforce Lightning modals, SPA dialogs).\n * Stored at module scope so the timer persists across observer callbacks.\n */\nlet orphanRescanTimer: ReturnType<typeof setTimeout> | null = null;\nconst ORPHAN_RESCAN_DEBOUNCE_MS = 500;\n\n/**\n * Secondary delayed rescan timer for complex SPAs (e.g. Salesforce Lightning)\n * where custom elements render their shadow DOM asynchronously, meaning the\n * 500ms debounce fires before all fields are ready.\n */\nlet orphanRescanDelayedTimer: ReturnType<typeof setTimeout> | null = null;\nconst ORPHAN_RESCAN_DELAYED_MS = 2000;\n\n/** Names of already-registered orphan tools. Prevents double-registration when\n * the observer fires multiple times for the same modal opening. */\nconst registeredOrphanToolNames = new Set<string>();\n\nfunction scheduleOrphanRescan(config: ResolvedConfig): void {\n if (orphanRescanTimer) clearTimeout(orphanRescanTimer);\n orphanRescanTimer = setTimeout(() => {\n orphanRescanTimer = null;\n void scanOrphanInputs(config);\n }, ORPHAN_RESCAN_DEBOUNCE_MS);\n}\n\n/**\n * Schedule a second, longer-delayed orphan rescan for custom elements that\n * render their shadow DOM asynchronously (e.g. Salesforce Lightning Web Components\n * populate their shadow roots after connectedCallback, which can take 1-2s).\n */\nfunction scheduleOrphanRescanDelayed(config: ResolvedConfig): void {\n if (orphanRescanDelayedTimer) clearTimeout(orphanRescanDelayedTimer);\n orphanRescanDelayedTimer = setTimeout(() => {\n orphanRescanDelayedTimer = null;\n void scanOrphanInputs(config);\n }, ORPHAN_RESCAN_DELAYED_MS);\n}\n\n/** Returns true if node is (or contains) an input-like or ARIA-role element. */\nfunction isInterestingNode(node: Element): boolean {\n const tag = node.tagName.toLowerCase();\n if (tag === 'input' || tag === 'textarea' || tag === 'select') return true;\n // Custom elements (e.g. Salesforce LWC: lightning-input, lightning-record-edit-form)\n // render their inputs inside shadow DOM which querySelectorAll() cannot pierce.\n // Treat any custom element as potentially interesting so we schedule a shadow rescan.\n if (tag.includes('-')) return true;\n const role = node.getAttribute('role');\n if (role && (ARIA_ROLES_TO_SCAN as readonly string[]).includes(role)) return true;\n if (node.querySelector('input, textarea, select')) return true;\n for (const r of ARIA_ROLES_TO_SCAN) {\n if (node.querySelector(`[role=\"${r}\"]`)) return true;\n }\n return false;\n}\n\nfunction scheduleReAnalysis(form: HTMLFormElement, config: ResolvedConfig): void {\n const existing = reAnalysisTimers.get(form);\n if (existing) clearTimeout(existing);\n reAnalysisTimers.set(\n form,\n setTimeout(() => {\n reAnalysisTimers.delete(form);\n void registerForm(form, config);\n }, RE_ANALYSIS_DEBOUNCE_MS),\n );\n}\n\nfunction scheduleFormReAnalysisById(formId: string, config: ResolvedConfig): void {\n const owner = document.getElementById(formId);\n if (owner instanceof HTMLFormElement && registeredForms.has(owner)) {\n scheduleReAnalysis(owner, config);\n }\n}\n\nfunction resolveOwnerForm(el: Element): HTMLFormElement | null {\n const closest = el.closest('form');\n if (closest instanceof HTMLFormElement) return closest;\n const explicitOwner = (el as HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | HTMLButtonElement).form;\n if (explicitOwner instanceof HTMLFormElement) return explicitOwner;\n const formId = el.getAttribute('form');\n if (formId) {\n const byId = document.getElementById(formId);\n if (byId instanceof HTMLFormElement) return byId;\n }\n return null;\n}\n\nfunction startObserver(config: ResolvedConfig): void {\n if (observer) return;\n\n observer = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n if (mutation.type === 'attributes' && mutation.target instanceof Element) {\n const target = mutation.target;\n const ownerForm = resolveOwnerForm(target);\n if (ownerForm && registeredForms.has(ownerForm)) {\n scheduleReAnalysis(ownerForm, config);\n } else if (target instanceof HTMLFormElement) {\n void registerForm(target, config);\n } else if (isInterestingNode(target) && !target.closest('form')) {\n scheduleOrphanRescan(config);\n }\n\n if (mutation.attributeName === 'form' && mutation.oldValue) {\n scheduleFormReAnalysisById(mutation.oldValue, config);\n }\n continue;\n }\n\n for (const node of mutation.addedNodes) {\n if (!(node instanceof Element)) continue;\n\n if (node instanceof HTMLFormElement) {\n void registerForm(node, config);\n continue;\n }\n\n // Newly added child inside an already-registered form?\n const parentForm = node.closest('form');\n if (parentForm instanceof HTMLFormElement && registeredForms.has(parentForm) && isInterestingNode(node)) {\n scheduleReAnalysis(parentForm, config);\n }\n\n // New forms nested inside the added subtree\n for (const form of Array.from(node.querySelectorAll<HTMLFormElement>('form'))) {\n void registerForm(form, config);\n }\n\n // If the added node contains inputs that are NOT inside any <form>,\n // schedule a debounced orphan re-scan. This covers SPAs like Salesforce\n // Lightning where a modal with inputs is injected after initial page load.\n if (isInterestingNode(node) && !node.closest('form')) {\n scheduleOrphanRescan(config);\n // Custom elements (tag names with hyphens, e.g. lightning-record-edit-form)\n // populate their shadow DOM asynchronously after connectedCallback.\n // Fire a second scan at 2s to catch fields that weren't ready at 500ms.\n if (node.tagName.toLowerCase().includes('-')) {\n scheduleOrphanRescanDelayed(config);\n }\n }\n }\n\n for (const node of mutation.removedNodes) {\n if (!(node instanceof Element)) continue;\n\n const forms = node instanceof HTMLFormElement\n ? [node]\n : Array.from(node.querySelectorAll<HTMLFormElement>('form'));\n\n for (const form of forms) {\n void unregisterForm(form, config);\n }\n }\n }\n });\n\n observer.observe(document.body, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeOldValue: true,\n });\n}\n\n// ---------------------------------------------------------------------------\n// SPA route change support\n// ---------------------------------------------------------------------------\n\nfunction listenForRouteChanges(config: ResolvedConfig): void {\n // Hash changes\n window.addEventListener('hashchange', () => scanForms(config));\n\n // History API (pushState / replaceState)\n const original = {\n pushState: history.pushState.bind(history),\n replaceState: history.replaceState.bind(history),\n };\n\n history.pushState = function (...args) {\n original.pushState(...args);\n scanForms(config);\n };\n\n history.replaceState = function (...args) {\n original.replaceState(...args);\n scanForms(config);\n };\n\n window.addEventListener('popstate', () => scanForms(config));\n}\n\n// ---------------------------------------------------------------------------\n// Main scan\n// ---------------------------------------------------------------------------\n\nasync function scanForms(config: ResolvedConfig): Promise<void> {\n const forms = Array.from(document.querySelectorAll<HTMLFormElement>('form'));\n await Promise.allSettled(forms.map((form) => registerForm(form, config)));\n}\n\n// ---------------------------------------------------------------------------\n// Orphan input scanner (fallback for pages with no <form> elements)\n// ---------------------------------------------------------------------------\n\n/** Input types that are never useful to expose to agents */\nconst ORPHAN_EXCLUDED_TYPES = new Set([\n 'password', 'hidden', 'file', 'submit', 'reset', 'button', 'image',\n]);\n\n/**\n * Recursively collect input-like elements from shadow roots throughout the document.\n * Returns an array of `{ el, shadowHost }` pairs where `shadowHost` is the outermost\n * shadow-host element that lives in the regular (non-shadow) DOM. This is used as\n * the anchor for grouping inputs by their nearest submit button.\n *\n * This covers Salesforce Lightning Web Components where fields like `lightning-input`\n * render a native `<input>` inside nested shadow roots invisible to querySelectorAll().\n */\nfunction collectShadowOrphanInputs(\n root: Element | ShadowRoot,\n outerHost: Element | null,\n visited = new Set<Element | ShadowRoot>(),\n): Array<{ el: HTMLElement; shadowHost: Element }> {\n if (visited.has(root)) return [];\n visited.add(root);\n\n const results: Array<{ el: HTMLElement; shadowHost: Element }> = [];\n\n for (const el of Array.from(root.querySelectorAll('*'))) {\n const sr = el.shadowRoot;\n if (!sr) continue;\n\n // The outermost host in the regular DOM is the first element that has a\n // shadow root encountered while walking from the document root.\n const host = outerHost ?? el;\n results.push(...collectShadowOrphanInputs(sr, host, visited));\n }\n\n // Collect inputs directly inside this shadow root level.\n if (root instanceof ShadowRoot) {\n const selector =\n 'input, textarea, select, ' +\n '[role=\"textbox\"]:not(input):not(textarea), ' +\n '[role=\"searchbox\"]:not(input):not(textarea), ' +\n 'button[role=\"combobox\"]';\n for (const el of Array.from(root.querySelectorAll<HTMLElement>(selector))) {\n if (el instanceof HTMLInputElement && ORPHAN_EXCLUDED_TYPES.has(el.type.toLowerCase())) continue;\n // Skip if the element is inside a <form> within this shadow root.\n if (el.closest('form')) continue;\n const rect = el.getBoundingClientRect();\n if (rect.width === 0 || rect.height === 0) continue;\n if (outerHost) {\n results.push({ el, shadowHost: outerHost });\n }\n }\n }\n\n return results;\n}\n\n/**\n * Find all visible form controls that are NOT inside a <form> element,\n * group them by their nearest ancestor that also contains a submit button,\n * and register each group as a WebMCP tool.\n *\n * This covers common patterns on landing pages and Ghost blogs where the\n * subscribe/search UI is built from plain inputs + buttons without a <form> tag.\n */\nasync function scanOrphanInputs(config: ResolvedConfig): Promise<void> {\n if (!isWebMCPSupported()) return;\n\n // Matches enabled submit buttons: traditional [type=\"submit\"] plus primary-variant buttons\n // used by React design systems (GitHub/Primer: data-variant=\"primary\") that use type=\"button\"\n // with JavaScript click handlers instead of native form submission.\n const SUBMIT_BTN_SELECTOR = '[type=\"submit\"]:not([disabled]), button[data-variant=\"primary\"]:not([disabled])';\n // Includes disabled variants \u2014 used only to identify which container is the \"form group\".\n // Many sites (GitHub) start with the submit button disabled until fields are filled.\n const SUBMIT_BTN_GROUPING_SELECTOR = '[type=\"submit\"], button[data-variant=\"primary\"]';\n const SUBMIT_TEXT_RE = /subscribe|submit|sign[\\s-]?up|send|join|go|search|post|tweet|publish|save/i;\n\n // Collect visible inputs that are not inside a <form>.\n // Includes native controls AND ARIA textbox/contenteditable elements (e.g. Twitter/X\n // compose box uses role=\"textbox\" on a contenteditable div, not a native <textarea>).\n const orphanInputs = (Array.from(\n document.querySelectorAll(\n 'input:not(form input), textarea:not(form textarea), select:not(form select), ' +\n '[role=\"textbox\"]:not(form [role=\"textbox\"]):not(input):not(textarea), ' +\n '[role=\"searchbox\"]:not(form [role=\"searchbox\"]):not(input):not(textarea), ' +\n '[contenteditable=\"true\"]:not(form [contenteditable=\"true\"]):not(input):not(textarea), ' +\n // button[role=\"combobox\"] covers JS-powered dropdowns (Salesforce Lightning Stage/Type/Lead Source,\n // Atlaskit Select) where a <button> opens a listbox instead of a native <select>.\n 'button[role=\"combobox\"]:not(form button[role=\"combobox\"])',\n ),\n ) as Array<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | HTMLElement>).filter((el) => {\n if (el instanceof HTMLInputElement && ORPHAN_EXCLUDED_TYPES.has(el.type.toLowerCase())) {\n console.log(`[auto-webmcp] orphan: skipping excluded type \"${el.type}\" (name=\"${el.name}\" id=\"${el.id}\")`);\n return false;\n }\n const rect = el.getBoundingClientRect();\n if (rect.width === 0 || rect.height === 0) {\n console.log(`[auto-webmcp] orphan: skipping invisible input (name=\"${(el as HTMLElement & { name?: string }).name}\" id=\"${el.id}\")`);\n return false;\n }\n return true;\n });\n\n // Also collect inputs buried inside shadow DOM (e.g. Salesforce LWC lightning-input components).\n const shadowOrphans = collectShadowOrphanInputs(document.body, null);\n console.log(`[auto-webmcp] orphan: found ${orphanInputs.length} light-DOM + ${shadowOrphans.length} shadow-DOM orphan inputs`);\n\n if (orphanInputs.length === 0 && shadowOrphans.length === 0) return;\n\n // Group inputs by the nearest ancestor that also contains a submit button.\n // Walk up from each input until we find a container with a submit-like button.\n const groups = new Map<Element, Array<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | HTMLElement>>();\n\n for (const input of orphanInputs) {\n let container: Element | null = input.parentElement;\n let foundContainer: Element = input.parentElement ?? document.body;\n\n while (container && container !== document.body) {\n const hasSubmitBtn =\n container.querySelector(SUBMIT_BTN_GROUPING_SELECTOR) !== null ||\n Array.from(container.querySelectorAll('button')).some(\n (b) => SUBMIT_TEXT_RE.test(b.textContent ?? ''),\n );\n if (hasSubmitBtn) {\n foundContainer = container;\n break;\n }\n container = container.parentElement;\n }\n\n console.log(`[auto-webmcp] orphan: input (name=\"${(input as HTMLElement & { name?: string }).name}\" id=\"${input.id}\") grouped into container`, foundContainer);\n if (!groups.has(foundContainer)) groups.set(foundContainer, []);\n groups.get(foundContainer)!.push(input);\n }\n\n // Group shadow DOM inputs by walking up from their outermost shadow host.\n for (const { el, shadowHost } of shadowOrphans) {\n let container: Element | null = shadowHost.parentElement;\n let foundContainer: Element = shadowHost.parentElement ?? document.body;\n\n while (container && container !== document.body) {\n const hasSubmitBtn =\n container.querySelector(SUBMIT_BTN_GROUPING_SELECTOR) !== null ||\n Array.from(container.querySelectorAll('button')).some(\n (b) => SUBMIT_TEXT_RE.test(b.textContent ?? ''),\n );\n if (hasSubmitBtn) {\n foundContainer = container;\n break;\n }\n container = container.parentElement;\n }\n\n console.log(`[auto-webmcp] orphan (shadow): input (id=\"${el.id}\") via host <${shadowHost.tagName.toLowerCase()}> grouped into container`, foundContainer);\n if (!groups.has(foundContainer)) groups.set(foundContainer, []);\n groups.get(foundContainer)!.push(el);\n }\n\n console.log(`[auto-webmcp] orphan: ${groups.size} group(s) found`);\n\n for (const [container, inputs] of groups) {\n // Pick the last visible submit button within the container (same logic as\n // handleCallTool in background.js: primary action is always the last one).\n const allCandidates = Array.from(\n container.querySelectorAll<HTMLButtonElement | HTMLInputElement>(SUBMIT_BTN_SELECTOR),\n ).filter((b) => {\n const r = b.getBoundingClientRect();\n return r.width > 0 && r.height > 0;\n });\n\n let submitBtn: HTMLButtonElement | HTMLInputElement | null =\n (allCandidates[allCandidates.length - 1] as HTMLButtonElement | HTMLInputElement) ?? null;\n\n // Fallback 1: disabled [type=\"submit\"] buttons in the container.\n // Many sites (GitHub new-issue form) start with the submit button disabled until\n // required fields are filled. Use the disabled button as the reference for tool\n // naming; the execute handler will poll for it to become enabled before clicking.\n if (!submitBtn) {\n const disabledCandidates = Array.from(\n container.querySelectorAll<HTMLButtonElement | HTMLInputElement>(SUBMIT_BTN_GROUPING_SELECTOR),\n ).filter((b) => {\n const r = b.getBoundingClientRect();\n return r.width > 0 && r.height > 0 && (b as HTMLButtonElement).disabled;\n });\n submitBtn = (disabledCandidates[disabledCandidates.length - 1] as HTMLButtonElement | HTMLInputElement) ?? null;\n if (submitBtn) console.log(`[auto-webmcp] orphan: using disabled submit button as reference: \"${submitBtn.textContent?.trim()}\"`);\n }\n\n // Fallback 2: any visible button with submit-like text WITHIN the container.\n // Catches React-style buttons (Twitter \"Post\", LinkedIn \"Share\") that use\n // type=\"button\" with no variant class, scoped to the container so we don't\n // pick up unrelated buttons elsewhere on the page (e.g. sidebar).\n // Also includes [role=\"button\"] to cover div/span-based buttons (Gmail Send).\n if (!submitBtn) {\n const containerBtns = Array.from(\n container.querySelectorAll<HTMLElement>('button, [role=\"button\"]'),\n ).filter((b) => {\n const r = b.getBoundingClientRect();\n return r.width > 0 && r.height > 0 &&\n !(b as HTMLButtonElement).disabled &&\n b.getAttribute('aria-disabled') !== 'true' &&\n SUBMIT_TEXT_RE.test(b.textContent ?? '');\n });\n submitBtn = (containerBtns[containerBtns.length - 1] as HTMLButtonElement | null) ?? null;\n if (submitBtn) console.log(`[auto-webmcp] orphan: using text-matched button in container: \"${submitBtn.textContent?.trim()}\"`);\n }\n\n // Fallback 3: nearest dialog/modal ancestor \u2014 catches buttons like LinkedIn's\n // \"Post\" that sit outside the input container but inside the same dialog.\n // Two-pass: first try ANY disabled button (HTML disabled OR aria-disabled) that\n // matches the submit keyword. Disabled buttons in compose dialogs are almost\n // always the real submit button waiting for content, not settings/config buttons\n // (which are always enabled). If no disabled ones, fall back to enabled buttons.\n if (!submitBtn) {\n const dialog = container.closest('[role=\"dialog\"], [aria-modal=\"true\"]');\n if (dialog) {\n const allDialogBtns = Array.from(\n dialog.querySelectorAll<HTMLElement>('button, [role=\"button\"]'),\n ).filter((b) => {\n const r = b.getBoundingClientRect();\n return r.width > 0 && r.height > 0 && SUBMIT_TEXT_RE.test(b.textContent ?? '');\n });\n console.log(`[auto-webmcp] orphan: dialog buttons matching submit text:`,\n allDialogBtns.map(b => `\"${b.textContent?.trim().slice(0, 30)}\" disabled=${(b as HTMLButtonElement).disabled} aria-disabled=${b.getAttribute('aria-disabled')}`));\n // Pass 1: any form of disabled (HTML or ARIA) \u2014 real submit button waiting for input\n const disabledBtns = allDialogBtns.filter(\n (b) => (b as HTMLButtonElement).disabled || b.getAttribute('aria-disabled') === 'true',\n );\n // Pass 2: enabled buttons \u2014 settings/config buttons are always enabled\n const enabledBtns = allDialogBtns.filter(\n (b) => !(b as HTMLButtonElement).disabled && b.getAttribute('aria-disabled') !== 'true',\n );\n const dialogBtns = disabledBtns.length > 0 ? disabledBtns : enabledBtns;\n submitBtn = (dialogBtns[dialogBtns.length - 1] as HTMLButtonElement | null) ?? null;\n if (submitBtn) console.log(`[auto-webmcp] orphan: using text-matched button in dialog: \"${submitBtn.textContent?.trim().slice(0, 40)}\" disabled=${(submitBtn as HTMLButtonElement).disabled} aria-disabled=${submitBtn.getAttribute('aria-disabled')}`);\n }\n }\n\n // Fallback 4: nearest button with submit-like text anywhere on the page.\n // Includes [role=\"button\"] for div/span-based buttons (Gmail, Outlook, etc.).\n if (!submitBtn) {\n const pageBtns = Array.from(\n document.querySelectorAll<HTMLElement>('button, [role=\"button\"]'),\n ).filter((b) => {\n const r = b.getBoundingClientRect();\n return r.width > 0 && r.height > 0 &&\n b.getAttribute('aria-disabled') !== 'true' &&\n SUBMIT_TEXT_RE.test(b.textContent ?? '');\n });\n submitBtn = (pageBtns[pageBtns.length - 1] as HTMLButtonElement | null) ?? null;\n if (submitBtn) console.log(`[auto-webmcp] orphan: using page-wide fallback submit button: \"${submitBtn.textContent?.trim()}\"`);\n }\n\n console.log(`[auto-webmcp] orphan: submit button for group:`, submitBtn ? `\"${submitBtn.textContent?.trim()}\" disabled=${(submitBtn as HTMLButtonElement).disabled}` : 'none');\n\n const metadata = analyzeOrphanInputGroup(container, inputs, submitBtn);\n // Same orphan group can be discovered repeatedly; skip duplicates by base name.\n if (registeredOrphanToolNames.has(metadata.name)) {\n console.log(`[auto-webmcp] orphan: \"${metadata.name}\" already registered, skipping`);\n continue;\n }\n const orphanName = ensureUniqueToolName(metadata.name);\n if (orphanName !== metadata.name && config.debug) {\n console.warn(`[auto-webmcp] orphan tool name collision: \"${metadata.name}\" renamed to \"${orphanName}\"`);\n }\n metadata.name = orphanName;\n console.log(`[auto-webmcp] orphan: tool=\"${metadata.name}\" schema keys:`, Object.keys(metadata.inputSchema.properties));\n\n // Build key \u2192 element pairs for the execute handler\n const inputPairs: Array<{ key: string; el: HTMLElement }> = [];\n const schemaProps = metadata.inputSchema.properties;\n // Auto-generated IDs (React: _r_1_, _r_c_) are not meaningful schema keys.\n // Skip them so aria-label or placeholder can provide a better key.\n const AUTO_ID_RE = /^_r_[0-9a-z]+_$/i;\n for (const el of inputs) {\n const id = el.id && !AUTO_ID_RE.test(el.id) ? el.id : null;\n // Use getAttribute for name/placeholder so this works for both native controls\n // and ARIA/contenteditable elements (which lack .name and .placeholder properties).\n const key =\n (el as HTMLInputElement).name ||\n el.getAttribute('name') ||\n (el as HTMLElement).dataset['webmcpName'] ||\n id ||\n el.getAttribute('aria-label') ||\n el.getAttribute('placeholder') ||\n null;\n const safeKey = key\n ? key.toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, '').slice(0, 64)\n : null;\n const matched = !!(safeKey && schemaProps[safeKey]);\n console.log(`[auto-webmcp] orphan: field (name=\"${(el as HTMLInputElement).name ?? ''}\" id=\"${el.id}\") rawKey=\"${key}\" safeKey=\"${safeKey}\" matched=${matched}`);\n if (matched) {\n inputPairs.push({ key: safeKey!, el });\n }\n }\n\n console.log(`[auto-webmcp] orphan: ${inputPairs.length}/${inputs.length} input(s) mapped to schema keys`);\n\n if (inputPairs.length === 0) {\n console.log(`[auto-webmcp] orphan: skipping group \"${metadata.name}\" \u2014 no inputs mapped to schema keys`);\n continue;\n }\n\n const toolName = metadata.name;\n const execute = async (\n params: Record<string, unknown>,\n _client?: unknown,\n ): Promise<{ content: Array<{ type: 'text'; text: string }> }> => {\n console.log(`[auto-webmcp] orphan execute: tool=\"${toolName}\" params=`, params);\n console.log(`[auto-webmcp] orphan execute: inputPairs=`, inputPairs.map(p => p.key));\n\n for (const { key, el } of inputPairs) {\n if (params[key] !== undefined) {\n console.log(`[auto-webmcp] orphan execute: filling key=\"${key}\" value=`, params[key], 'element=', el);\n // button[role=\"combobox\"] (Salesforce Lightning, Atlaskit) requires async fill:\n // click to open listbox, wait for options to render, click the matching option.\n if (el.getAttribute('role') === 'combobox' && el.tagName.toLowerCase() === 'button') {\n await fillComboboxButton(el, params[key]);\n } else {\n fillElement(el, params[key]);\n }\n console.log(`[auto-webmcp] orphan execute: after fill, element value=`, (el as HTMLInputElement).value);\n } else {\n console.log(`[auto-webmcp] orphan execute: key=\"${key}\" not in params, skipping`);\n }\n }\n window.dispatchEvent(new CustomEvent('toolactivated', { detail: { toolName } }));\n\n const shouldAutoSubmit =\n config.autoSubmit ||\n !!submitBtn?.hasAttribute('toolautosubmit') ||\n (submitBtn instanceof HTMLElement && submitBtn.dataset['webmcpAutosubmit'] !== undefined) ||\n container.hasAttribute('toolautosubmit') ||\n (container instanceof HTMLElement && container.dataset['webmcpAutosubmit'] !== undefined);\n\n if (!shouldAutoSubmit) {\n console.log(`[auto-webmcp] orphan execute: autoSubmit=false, returning without clicking submit`);\n return { content: [{ type: 'text', text: 'Fields filled. Ready to submit.' }] };\n }\n\n // Find the enabled submit button to click.\n // Priority: captured submitBtn reference (still in DOM + enabled) \u2192 selector poll (React/Vue\n // enable-on-valid pattern) \u2192 text-matched button (Salesforce type=\"button\" Save, Gmail Send).\n console.log(`[auto-webmcp] orphan execute: resolving submit button (up to 2s)...`);\n let btn: HTMLButtonElement | HTMLInputElement | HTMLElement | null = null;\n\n // Check the captured reference first (avoids 2s timeout for always-enabled buttons like Salesforce Save).\n if (submitBtn && document.contains(submitBtn)) {\n const isEnabled = !(submitBtn as HTMLButtonElement).disabled &&\n submitBtn.getAttribute('aria-disabled') !== 'true';\n const r = submitBtn.getBoundingClientRect();\n if (isEnabled && r.width > 0 && r.height > 0) {\n btn = submitBtn;\n console.log(`[auto-webmcp] orphan execute: using captured submit button \"${btn.textContent?.trim()}\"`);\n }\n }\n\n if (!btn) {\n // Poll for a [type=\"submit\"] or primary-variant button to become enabled\n // (handles React/Vue re-renders that enable the button after field validation).\n const deadline = Date.now() + 2000;\n while (Date.now() < deadline) {\n const candidates = Array.from(\n container.querySelectorAll<HTMLButtonElement | HTMLInputElement>(SUBMIT_BTN_SELECTOR),\n ).filter((b) => {\n const r = b.getBoundingClientRect();\n return r.width > 0 && r.height > 0;\n });\n const last = candidates[candidates.length - 1] ?? null;\n if (last) { btn = last; break; }\n await new Promise<void>((r) => setTimeout(r, 100));\n }\n }\n\n // Final fallback: any enabled text-matched button in the container or page.\n if (!btn) {\n const textBtns = Array.from(\n (container !== document.body ? container : document).querySelectorAll<HTMLElement>('button, [role=\"button\"]'),\n ).filter((b) => {\n const r = b.getBoundingClientRect();\n return r.width > 0 && r.height > 0 &&\n !(b as HTMLButtonElement).disabled &&\n b.getAttribute('aria-disabled') !== 'true' &&\n SUBMIT_TEXT_RE.test(b.textContent ?? '');\n });\n btn = textBtns[textBtns.length - 1] ?? null;\n if (btn) console.log(`[auto-webmcp] orphan execute: using text-matched fallback button \"${btn.textContent?.trim()}\"`);\n }\n\n if (!btn) {\n console.warn(`[auto-webmcp] orphan execute: submit button still disabled after 2s`);\n return { content: [{ type: 'text', text: 'Fields filled but the submit button is still disabled. The page may require additional input before submitting.' }] };\n }\n\n console.log(`[auto-webmcp] orphan execute: clicking submit button \"${(btn as HTMLElement).textContent?.trim()}\"`);\n (btn as HTMLElement).click();\n return { content: [{ type: 'text', text: 'Fields filled and form submitted.' }] };\n };\n\n try {\n const toolDef: {\n name: string;\n description: string;\n inputSchema: JsonSchema;\n annotations?: ToolAnnotations;\n execute: (\n params: Record<string, unknown>,\n client?: unknown,\n ) => Promise<{ content: Array<{ type: 'text'; text: string }> }>;\n } = {\n name: metadata.name,\n description: metadata.description,\n inputSchema: metadata.inputSchema,\n execute,\n };\n if (metadata.annotations && Object.keys(metadata.annotations).length > 0) {\n toolDef.annotations = metadata.annotations;\n }\n await navigator.modelContext!.registerTool(toolDef);\n registeredOrphanToolNames.add(metadata.name);\n // Expose the submit button reference so background.js can click it via CDP.\n // GitHub and other React apps use type=\"button\" (not type=\"submit\"), so\n // background.js cannot find the button by selector alone.\n const pendingBtns = ((window as unknown as Record<string, unknown>)['__pendingSubmitBtns'] ??= {}) as Record<string, Element | null>;\n pendingBtns[metadata.name] = submitBtn;\n if (config.debug) {\n console.log(`[auto-webmcp] Orphan tool registered: ${metadata.name}`, metadata);\n }\n } catch {\n // Best-effort\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nfunction warnToolQuality(name: string, description: string): void {\n if (/^form_\\d+$|^submit$|^form$/.test(name)) {\n console.warn(`[auto-webmcp] Tool \"${name}\" has a generic name. Consider adding a toolname or data-webmcp-name attribute.`);\n }\n if (!description || description === 'Submit form') {\n console.warn(`[auto-webmcp] Tool \"${name}\" has no meaningful description.`);\n }\n if (/don'?t|do not|never|avoid|not for/i.test(description)) {\n console.warn(`[auto-webmcp] Tool \"${name}\" description contains negative instructions. Per spec best practices, prefer positive descriptions.`);\n }\n}\n\nexport async function startDiscovery(config: ResolvedConfig): Promise<void> {\n if (document.readyState === 'loading') {\n await new Promise<void>((resolve) =>\n document.addEventListener('DOMContentLoaded', () => resolve(), { once: true }),\n );\n }\n\n registeredFormCount = 0;\n registeredOrphanToolNames.clear();\n startObserver(config);\n listenForRouteChanges(config);\n await scanForms(config);\n\n // Always scan for orphan inputs (inputs outside <form> elements) in addition to form-based tools.\n // Many modern SPAs (GitHub issues, Twitter compose, LinkedIn) render inputs without a <form> wrapper.\n await scanOrphanInputs(config);\n}\n\nexport function stopDiscovery(): void {\n observer?.disconnect();\n observer = null;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["src_exports", "__export", "autoWebMCP", "resolveConfig", "userConfig", "strict", "enableAliasResolution", "ARIA_ROLES_TO_SCAN", "inputTypeToSchema", "input", "mapInputElement", "mapSelectElement", "buildStringSchema", "prop", "listId", "datalist", "options", "o", "PLACEHOLDER_PATTERNS", "isPlaceholderOption", "opt", "select", "enumValues", "oneOf", "child", "groupLabel", "entry", "collectCheckboxEnum", "form", "name", "el", "cb", "v", "collectRadioEnum", "r", "collectRadioOneOf", "title", "getRadioLabelText", "ariaRoleToSchema", "role", "min", "max", "ownedId", "listbox", "radio", "parent", "clone", "text", "label", "formIndex", "analyzeForm", "form", "override", "name", "inferToolName", "description", "inferToolDescription", "inputSchema", "fieldElements", "buildSchema", "annotations", "inferAnnotations", "nativeName", "sanitizeName", "explicit", "submitText", "getSubmitButtonText", "heading", "getNearestHeadingText", "segment", "getLastPathSegment", "formIndex", "raw", "buttons", "btn", "text", "node", "sibling", "url", "segments", "nativeDesc", "legend", "ariaLabel", "describedById", "descEl", "pageTitle", "READONLY_BUTTON_PATTERNS", "DESTRUCTIVE_BUTTON_PATTERNS", "DESTRUCTIVE_URL_PATTERNS", "isGet", "isReadLabel", "isDestructiveLabel", "isDestructiveUrl", "extractDefaultValue", "control", "type", "selected", "o", "collectShadowControls", "root", "visited", "results", "el", "found", "f", "collectFormAssociatedControls", "controls", "seen", "shadowControl", "properties", "required", "processedRadioGroups", "processedCheckboxGroups", "fieldKey", "resolveNativeControlFallbackKey", "schemaProp", "inputTypeToSchema", "isControlVisible", "inferFieldTitle", "desc", "inferFieldDescription", "defaultVal", "collectRadioEnum", "radioOneOf", "collectRadioOneOf", "checkedRadio", "checkboxValues", "collectCheckboxEnum", "arrayProp", "checkedBoxes", "isRequired", "hostNode", "host", "ariaControls", "collectAriaControls", "processedAriaRadioGroups", "role", "key", "enumValues", "enumOneOf", "ariaRoleToSchema", "inferAriaFieldTitle", "inferAriaFieldDescription", "AUTO_GENERATED_ID_RE", "label", "hostKey", "resolveShadowHostKey", "fieldName", "hostLabel", "hostName", "resolveAriaElementKey", "placeholder", "selector", "ARIA_ROLES_TO_SCAN", "r", "rawResults", "resolveAriaFieldKey", "radioEntries", "e", "nonRadioEntries", "radioGroupMap", "ungroupedRadios", "entry", "group", "groupedEntries", "members", "groupKey", "val", "title", "htmlEl", "labelledById", "humanizeName", "nativeParamDesc", "ariaDesc", "labelText", "getAssociatedLabelText", "ph", "labelTextWithoutNested", "ownRoot", "shadowLabel", "anyLabel", "parent", "clone", "c", "style", "analyzeOrphanInputGroup", "container", "inputs", "submitBtn", "inferOrphanToolName", "inferOrphanToolDescription", "buildSchemaFromInputs", "inferOrphanAnnotations", "getNearestHeadingTextFrom", "inner", "prop", "rawName", "i", "cb", "v", "EXECUTE_OUTPUT_SCHEMA", "registeredTools", "registrationControllers", "isWebMCPSupported", "registerFormTool", "form", "metadata", "execute", "unregisterFormTool", "toolDef", "controller", "name", "getRegisteredToolName", "getAllRegisteredTools", "unregisterAll", "entries", "pendingExecutions", "lastParams", "formFieldElements", "pendingWarnings", "pendingFillWarnings", "lastFilledSnapshot", "preFillValues", "_inputValueSetter", "_textareaValueSetter", "_checkedSetter", "normalizeAliasKey", "raw", "addAlias", "index", "alias", "schemaKey", "normalized", "buildAliasIndex", "form", "metadata", "properties", "prop", "nativeEl", "findNativeField", "mappedEl", "el", "htmlEl", "label", "resolveParamsForSchema", "params", "config", "resolved", "warnings", "aliasEnabled", "key", "value", "aliasIndex", "rawKey", "candidates", "target", "collectInvalidFieldWarnings", "controls", "control", "field", "captureCurrentValues", "result", "data", "val", "existing", "collectValidationErrors", "errors", "v", "constraint", "buildExecuteHandler", "toolName", "attachSubmitInterceptor", "client", "modelContextClient", "resolve", "ok", "existingSnapshot", "resolvedParams", "aliasWarnings", "paramsToFill", "preserved", "current", "hasValue", "fillFormFields", "missingNow", "getMissingRequired", "reject", "timeoutMs", "timeoutId", "timedOutState", "warn", "_existingValsTimeout", "structured", "serializeFormData", "waitForDomStable", "attempt", "getResetFields", "submitForm", "found", "pending", "nextPending", "_existingValsBlocked", "err", "e", "formData", "existingVals", "missingRequired", "fillWarnings", "skippedFields", "w", "f", "allWarnMessages", "warningText", "setReactValue", "setter", "setReactChecked", "checked", "findInShadowRoots", "root", "selector", "host", "sr", "deeper", "getAssociatedInputsByName", "type", "name", "named", "firstObj", "esc", "light", "fieldEls", "snapshot", "input", "fillInput", "b", "fillSelectElement", "o", "ariaEl", "effectiveEl", "elId", "fresh", "fillAriaField", "allBoxes", "box", "num", "min", "max", "clamped", "radios", "radio", "select", "vals", "opt", "strVal", "lower", "byLabel", "role", "other", "range", "sel", "text", "inserted", "dt", "r2", "maybeConvertIsoDate", "isoMatch", "fieldHint", "year", "month", "day", "fillElement", "maxMs", "debounceMs", "settled", "debounceTimer", "settle", "observer", "reset", "expected", "fieldKey", "queryShadowAll", "results", "hosts", "fillComboboxButton", "ariaControlsId", "listbox", "deadline", "poll", "byId", "inShadow", "lightCandidate", "shadowCandidate", "lightOptions", "shadowOptions", "options", "lowerValue", "match", "dataValue", "ariaLabel", "optText", "emit", "type", "form", "toolName", "isExcluded", "config", "selector", "withNumericSuffix", "baseName", "n", "suffix", "getUsedToolNames", "excludeForm", "names", "registeredOrphanToolNames", "name", "getAllRegisteredTools", "ensureUniqueToolName", "used", "i", "candidate", "hasNativeDeclarativeTool", "registerForm", "previousName", "getRegisteredToolName", "unregisterFormTool", "mode", "override", "ovr", "metadata", "analyzeForm", "resolvedName", "warnToolQuality", "execute", "buildExecuteHandler", "registerFormTool", "registeredForms", "registeredFormCount", "formSubmitBtn", "pendingBtns", "unregisterForm", "observer", "reAnalysisTimers", "RE_ANALYSIS_DEBOUNCE_MS", "orphanRescanTimer", "ORPHAN_RESCAN_DEBOUNCE_MS", "orphanRescanDelayedTimer", "ORPHAN_RESCAN_DELAYED_MS", "scheduleOrphanRescan", "scanOrphanInputs", "scheduleOrphanRescanDelayed", "isInterestingNode", "node", "tag", "role", "ARIA_ROLES_TO_SCAN", "r", "scheduleReAnalysis", "existing", "scheduleFormReAnalysisById", "formId", "owner", "resolveOwnerForm", "el", "closest", "explicitOwner", "byId", "startObserver", "mutations", "mutation", "target", "ownerForm", "parentForm", "forms", "listenForRouteChanges", "scanForms", "original", "args", "ORPHAN_EXCLUDED_TYPES", "collectShadowOrphanInputs", "root", "outerHost", "visited", "results", "sr", "host", "rect", "isWebMCPSupported", "SUBMIT_BTN_SELECTOR", "SUBMIT_BTN_GROUPING_SELECTOR", "SUBMIT_TEXT_RE", "orphanInputs", "shadowOrphans", "groups", "input", "container", "foundContainer", "b", "shadowHost", "inputs", "allCandidates", "submitBtn", "disabledCandidates", "containerBtns", "dialog", "allDialogBtns", "disabledBtns", "enabledBtns", "dialogBtns", "pageBtns", "analyzeOrphanInputGroup", "orphanName", "inputPairs", "schemaProps", "AUTO_ID_RE", "id", "key", "safeKey", "matched", "params", "_client", "p", "fillComboboxButton", "fillElement", "btn", "isEnabled", "deadline", "candidates", "last", "textBtns", "toolDef", "description", "startDiscovery", "resolve", "stopDiscovery", "autoWebMCP", "config", "resolved", "resolveConfig", "isWebMCPSupported", "startDiscovery", "stopDiscovery", "unregisterAll", "getAllRegisteredTools"]
|
|
7
7
|
}
|