@forms.expert/sdk 0.1.2 → 0.1.3
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/core/index.cjs +1 -1
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +4 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.js +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/react/index.cjs +1 -1
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +4 -0
- package/dist/react/index.d.ts +4 -0
- package/dist/react/index.js +1 -1
- package/dist/react/index.js.map +1 -1
- package/dist/vanilla/index.cjs +1 -1
- package/dist/vanilla/index.cjs.map +1 -1
- package/dist/vanilla/index.global.js +1 -1
- package/dist/vanilla/index.global.js.map +1 -1
- package/dist/vanilla/index.js +1 -1
- package/dist/vanilla/index.js.map +1 -1
- package/dist/vue/index.cjs +2 -2
- package/dist/vue/index.cjs.map +1 -1
- package/dist/vue/index.d.cts +4 -0
- package/dist/vue/index.d.ts +4 -0
- package/dist/vue/index.js +2 -2
- package/dist/vue/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../vanilla/styles.ts","../../vanilla/renderer.ts","../../core/types.ts","../../core/api-client.ts","../../core/forms-sdk.ts","../../vanilla/widget.ts"],"sourcesContent":["import { FormStyling } from '../core/types';\n\nconst defaultStyling: FormStyling = {\n theme: 'light',\n primaryColor: '#3b82f6',\n backgroundColor: '#ffffff',\n textColor: '#1f2937',\n borderRadius: 'md',\n fontSize: 'md',\n buttonStyle: 'filled',\n labelPosition: 'top',\n};\n\nfunction getBorderRadius(radius: FormStyling['borderRadius']): string {\n switch (radius) {\n case 'none': return '0';\n case 'sm': return '0.125rem';\n case 'md': return '0.375rem';\n case 'lg': return '0.5rem';\n default: return '0.375rem';\n }\n}\n\nfunction getFontSize(size: FormStyling['fontSize']): string {\n switch (size) {\n case 'sm': return '0.875rem';\n case 'md': return '1rem';\n case 'lg': return '1.125rem';\n default: return '1rem';\n }\n}\n\nexport function generateFormStyles(styling: FormStyling = defaultStyling): string {\n const s = { ...defaultStyling, ...styling };\n const radius = getBorderRadius(s.borderRadius);\n const fontSize = getFontSize(s.fontSize);\n\n return `\n.forms-expert {\n font-family: system-ui, -apple-system, sans-serif;\n font-size: ${fontSize};\n color: ${s.textColor};\n background-color: ${s.backgroundColor};\n padding: 1.5rem;\n border-radius: ${radius};\n box-sizing: border-box;\n}\n\n.forms-expert * {\n box-sizing: border-box;\n}\n\n.forms-expert-group {\n margin-bottom: 1rem;\n ${s.labelPosition === 'left' ? 'display: flex; align-items: flex-start; gap: 1rem;' : ''}\n}\n\n.forms-expert-label {\n display: block;\n font-weight: 500;\n color: ${s.textColor};\n ${s.labelPosition === 'left' ? 'width: 33%; flex-shrink: 0; padding-top: 0.5rem;' : 'margin-bottom: 0.25rem;'}\n}\n\n.forms-expert-required {\n color: #ef4444;\n margin-left: 0.25rem;\n}\n\n.forms-expert-input-wrapper {\n ${s.labelPosition === 'left' ? 'flex: 1;' : ''}\n}\n\n.forms-expert-input,\n.forms-expert-textarea,\n.forms-expert-select {\n width: 100%;\n padding: 0.5rem 0.75rem;\n border: 1px solid ${s.theme === 'dark' ? '#4b5563' : '#d1d5db'};\n border-radius: ${radius};\n font-size: ${fontSize};\n font-family: inherit;\n background-color: ${s.theme === 'dark' ? '#374151' : '#ffffff'};\n color: ${s.textColor};\n transition: border-color 0.15s, box-shadow 0.15s;\n}\n\n.forms-expert-input:focus,\n.forms-expert-textarea:focus,\n.forms-expert-select:focus {\n outline: none;\n border-color: ${s.primaryColor};\n box-shadow: 0 0 0 2px ${s.primaryColor}33;\n}\n\n.forms-expert-input.forms-expert-error,\n.forms-expert-textarea.forms-expert-error,\n.forms-expert-select.forms-expert-error {\n border-color: #ef4444;\n}\n\n.forms-expert-textarea {\n min-height: 100px;\n resize: vertical;\n}\n\n.forms-expert-checkbox-group {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n margin-bottom: 1rem;\n}\n\n.forms-expert-checkbox {\n width: 1rem;\n height: 1rem;\n accent-color: ${s.primaryColor};\n cursor: pointer;\n}\n\n.forms-expert-file-wrapper {\n position: relative;\n}\n\n.forms-expert-file {\n width: 100%;\n padding: 0.5rem 0.75rem;\n border: 1px solid ${s.theme === 'dark' ? '#4b5563' : '#d1d5db'};\n border-radius: ${radius};\n font-size: ${fontSize};\n background-color: ${s.theme === 'dark' ? '#374151' : '#ffffff'};\n cursor: pointer;\n}\n\n.forms-expert-error-message {\n color: #ef4444;\n font-size: 0.875rem;\n margin-top: 0.25rem;\n}\n\n.forms-expert-button {\n width: 100%;\n padding: 0.625rem 1.25rem;\n margin-top: 1rem;\n font-weight: 500;\n font-size: ${fontSize};\n font-family: inherit;\n border-radius: ${radius};\n cursor: pointer;\n transition: opacity 0.2s, transform 0.1s;\n ${s.buttonStyle === 'filled' \n ? `background-color: ${s.primaryColor}; color: white; border: none;`\n : `background-color: transparent; color: ${s.primaryColor}; border: 2px solid ${s.primaryColor};`\n }\n}\n\n.forms-expert-button:hover {\n opacity: 0.9;\n}\n\n.forms-expert-button:active {\n transform: scale(0.98);\n}\n\n.forms-expert-button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n transform: none;\n}\n\n.forms-expert-button-loading {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n}\n\n.forms-expert-spinner {\n width: 1rem;\n height: 1rem;\n border: 2px solid transparent;\n border-top-color: currentColor;\n border-radius: 50%;\n animation: forms-expert-spin 0.6s linear infinite;\n}\n\n@keyframes forms-expert-spin {\n to { transform: rotate(360deg); }\n}\n\n.forms-expert-honeypot {\n position: absolute;\n left: -9999px;\n opacity: 0;\n}\n\n.forms-expert-success {\n text-align: center;\n padding: 2rem;\n color: ${s.textColor};\n}\n\n.forms-expert-success-icon {\n width: 3rem;\n height: 3rem;\n margin: 0 auto 1rem;\n color: #22c55e;\n}\n\n.forms-expert-success-message {\n font-size: 1.125rem;\n font-weight: 500;\n margin-bottom: 0.5rem;\n}\n\n.forms-expert-branding {\n text-align: center;\n margin-top: 1rem;\n padding-top: 0.75rem;\n border-top: 1px solid ${s.theme === 'dark' ? '#374151' : '#e5e7eb'};\n}\n\n.forms-expert-branding a {\n color: ${s.theme === 'dark' ? '#9ca3af' : '#6b7280'};\n text-decoration: none;\n font-size: 0.75rem;\n}\n\n.forms-expert-branding a:hover {\n text-decoration: underline;\n}\n\n${s.customCss || ''}\n`.trim();\n}\n","import { FormField, FormSchema, ValidationError } from '../core/types';\n\n/**\n * Escape HTML special characters\n */\nfunction escapeHtml(text: string): string {\n const div = document.createElement('div');\n div.textContent = text;\n return div.innerHTML;\n}\n\n/**\n * Layout field types that don't capture data\n */\nconst LAYOUT_TYPES = new Set(['heading', 'divider', 'paragraph']);\n\n/**\n * Create a form field element\n */\nexport function renderField(\n field: FormField,\n value: unknown,\n error?: string\n): HTMLElement {\n const group = document.createElement('div');\n\n // Layout fields\n if (field.type === 'heading') {\n group.className = 'forms-expert-group';\n const h = document.createElement('h3');\n h.className = 'forms-expert-heading';\n h.textContent = field.label || '';\n group.appendChild(h);\n if (field.content) {\n const p = document.createElement('p');\n p.className = 'forms-expert-heading-subtitle';\n p.textContent = field.content;\n group.appendChild(p);\n }\n return group;\n }\n\n if (field.type === 'divider') {\n const hr = document.createElement('hr');\n hr.className = 'forms-expert-divider';\n return hr;\n }\n\n if (field.type === 'paragraph') {\n group.className = 'forms-expert-group';\n if (field.label) {\n const p = document.createElement('p');\n p.className = 'forms-expert-paragraph-label';\n p.textContent = field.label;\n group.appendChild(p);\n }\n if (field.content) {\n const p = document.createElement('p');\n p.className = 'forms-expert-paragraph';\n p.textContent = field.content;\n group.appendChild(p);\n }\n return group;\n }\n\n // Hidden field\n if (field.type === 'hidden') {\n const input = document.createElement('input');\n input.type = 'hidden';\n input.name = field.name;\n input.value = String(field.defaultValue ?? value ?? '');\n group.appendChild(input);\n group.style.display = 'none';\n return group;\n }\n\n // Checkbox / toggle / consent\n if (field.type === 'checkbox' || field.type === 'toggle' || field.type === 'consent') {\n group.className = 'forms-expert-checkbox-group';\n \n const input = document.createElement('input');\n input.type = 'checkbox';\n input.id = `mira-field-${field.name}`;\n input.name = field.name;\n input.className = 'forms-expert-checkbox';\n input.checked = Boolean(value);\n if (field.required) input.required = true;\n \n const label = document.createElement('label');\n label.htmlFor = input.id;\n const text = field.type === 'consent' ? (field.consentText || field.label || field.name) : (field.label || field.name);\n label.innerHTML = `${escapeHtml(text)}${field.required ? '<span class=\"forms-expert-required\">*</span>' : ''}`;\n \n group.appendChild(input);\n group.appendChild(label);\n\n if (field.type === 'consent' && field.consentUrl) {\n const link = document.createElement('a');\n link.href = field.consentUrl;\n link.target = '_blank';\n link.rel = 'noopener noreferrer';\n link.textContent = 'View policy';\n link.className = 'forms-expert-consent-link';\n group.appendChild(link);\n }\n\n if (error) {\n const errorEl = document.createElement('div');\n errorEl.className = 'forms-expert-error-message';\n errorEl.textContent = error;\n group.appendChild(errorEl);\n }\n return group;\n }\n\n // All other fields\n group.className = 'forms-expert-group';\n \n // Label\n const label = document.createElement('label');\n label.className = 'forms-expert-label';\n label.htmlFor = `mira-field-${field.name}`;\n label.innerHTML = `${escapeHtml(field.label || field.name)}${field.required ? '<span class=\"forms-expert-required\">*</span>' : ''}`;\n group.appendChild(label);\n \n // Input wrapper\n const wrapper = document.createElement('div');\n wrapper.className = 'forms-expert-input-wrapper';\n \n let input: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement;\n \n switch (field.type) {\n case 'textarea':\n case 'richText':\n input = document.createElement('textarea');\n input.className = 'forms-expert-textarea';\n input.value = String(value || '');\n if (field.maxLength) input.maxLength = field.maxLength;\n break;\n \n case 'select':\n case 'dropdown': {\n input = document.createElement('select');\n input.className = 'forms-expert-select';\n \n const defaultOption = document.createElement('option');\n defaultOption.value = '';\n defaultOption.textContent = field.placeholder || 'Select an option...';\n input.appendChild(defaultOption);\n \n const opts = (field.options || []) as string[];\n opts.forEach((opt) => {\n const option = document.createElement('option');\n option.value = opt;\n option.textContent = opt;\n if (value === opt) option.selected = true;\n input.appendChild(option);\n });\n break;\n }\n\n case 'radio': {\n // Render as radio group\n const radioGroup = document.createElement('div');\n radioGroup.className = 'forms-expert-radio-group';\n const opts = (field.options || []) as string[];\n opts.forEach((opt) => {\n const radioWrapper = document.createElement('label');\n radioWrapper.className = 'forms-expert-radio-item';\n const radioInput = document.createElement('input');\n radioInput.type = 'radio';\n radioInput.name = field.name;\n radioInput.value = opt;\n radioInput.checked = value === opt;\n radioWrapper.appendChild(radioInput);\n radioWrapper.appendChild(document.createTextNode(` ${opt}`));\n radioGroup.appendChild(radioWrapper);\n });\n wrapper.appendChild(radioGroup);\n if (error) {\n const errorEl = document.createElement('div');\n errorEl.className = 'forms-expert-error-message';\n errorEl.textContent = error;\n wrapper.appendChild(errorEl);\n }\n group.appendChild(wrapper);\n return group;\n }\n\n case 'multiselect': {\n const checkGroup = document.createElement('div');\n checkGroup.className = 'forms-expert-multiselect-group';\n const selected = (value as string[]) || [];\n const opts = (field.options || []) as string[];\n opts.forEach((opt) => {\n const checkWrapper = document.createElement('label');\n checkWrapper.className = 'forms-expert-checkbox-item';\n const checkInput = document.createElement('input');\n checkInput.type = 'checkbox';\n checkInput.name = field.name;\n checkInput.value = opt;\n checkInput.checked = selected.includes(opt);\n checkWrapper.appendChild(checkInput);\n checkWrapper.appendChild(document.createTextNode(` ${opt}`));\n checkGroup.appendChild(checkWrapper);\n });\n wrapper.appendChild(checkGroup);\n if (error) {\n const errorEl = document.createElement('div');\n errorEl.className = 'forms-expert-error-message';\n errorEl.textContent = error;\n wrapper.appendChild(errorEl);\n }\n group.appendChild(wrapper);\n return group;\n }\n\n case 'rating': {\n const ratingContainer = document.createElement('div');\n ratingContainer.className = 'forms-expert-rating';\n const max = field.ratingMax || 5;\n const current = (value as number) || 0;\n for (let i = 1; i <= max; i++) {\n const star = document.createElement('button');\n star.type = 'button';\n star.className = `forms-expert-rating-star ${i <= current ? 'active' : ''}`;\n star.textContent = '★';\n star.dataset.value = String(i);\n ratingContainer.appendChild(star);\n }\n wrapper.appendChild(ratingContainer);\n if (error) {\n const errorEl = document.createElement('div');\n errorEl.className = 'forms-expert-error-message';\n errorEl.textContent = error;\n wrapper.appendChild(errorEl);\n }\n group.appendChild(wrapper);\n return group;\n }\n\n case 'scale':\n case 'opinionScale': {\n const scaleContainer = document.createElement('div');\n scaleContainer.className = 'forms-expert-scale';\n const min = field.min ?? (field.type === 'opinionScale' ? 0 : 1);\n const max = field.max ?? (field.type === 'opinionScale' ? 10 : 5);\n const current = value as number | undefined;\n for (let i = min; i <= max; i++) {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = `forms-expert-scale-btn ${current === i ? 'active' : ''}`;\n btn.textContent = String(i);\n btn.dataset.value = String(i);\n scaleContainer.appendChild(btn);\n }\n wrapper.appendChild(scaleContainer);\n if (field.lowLabel || field.highLabel) {\n const labels = document.createElement('div');\n labels.className = 'forms-expert-scale-labels';\n labels.innerHTML = `<span>${escapeHtml(field.lowLabel || '')}</span><span>${escapeHtml(field.highLabel || '')}</span>`;\n wrapper.appendChild(labels);\n }\n if (error) {\n const errorEl = document.createElement('div');\n errorEl.className = 'forms-expert-error-message';\n errorEl.textContent = error;\n wrapper.appendChild(errorEl);\n }\n group.appendChild(wrapper);\n return group;\n }\n\n case 'slider': {\n input = document.createElement('input');\n input.type = 'range';\n input.className = 'forms-expert-slider';\n input.min = String(field.min ?? 0);\n input.max = String(field.max ?? 100);\n input.step = String(field.step ?? 1);\n input.value = String(value ?? field.min ?? 0);\n break;\n }\n\n case 'file':\n input = document.createElement('input');\n input.type = 'file';\n input.className = 'forms-expert-file';\n if (field.allowedMimeTypes?.length) {\n input.accept = field.allowedMimeTypes.join(',');\n }\n if (field.multiple) {\n input.multiple = true;\n }\n break;\n\n case 'currency': {\n input = document.createElement('input');\n input.type = 'number';\n input.className = 'forms-expert-input';\n input.value = String(value ?? '');\n if (field.min !== undefined) input.min = String(field.min);\n if (field.max !== undefined) input.max = String(field.max);\n input.step = String(field.step || 0.01);\n break;\n }\n\n case 'phone':\n input = document.createElement('input');\n input.type = 'tel';\n input.className = 'forms-expert-input';\n input.value = String(value || '');\n break;\n\n case 'url':\n input = document.createElement('input');\n input.type = 'url';\n input.className = 'forms-expert-input';\n input.value = String(value || '');\n break;\n\n case 'password':\n input = document.createElement('input');\n input.type = 'password';\n input.className = 'forms-expert-input';\n input.value = String(value || '');\n break;\n\n case 'time':\n input = document.createElement('input');\n input.type = 'time';\n input.className = 'forms-expert-input';\n input.value = String(value || '');\n break;\n\n case 'datetime':\n input = document.createElement('input');\n input.type = 'datetime-local';\n input.className = 'forms-expert-input';\n input.value = String(value || '');\n break;\n\n case 'colorPicker':\n input = document.createElement('input');\n input.type = 'color';\n input.className = 'forms-expert-color';\n input.value = String(value || '#000000');\n break;\n\n case 'dateRange': {\n // Two date inputs\n const rangeContainer = document.createElement('div');\n rangeContainer.className = 'forms-expert-date-range';\n const range = (value as { start?: string; end?: string }) || {};\n const startInput = document.createElement('input');\n startInput.type = 'date';\n startInput.className = 'forms-expert-input';\n startInput.name = `${field.name}.start`;\n startInput.value = range.start || '';\n const endInput = document.createElement('input');\n endInput.type = 'date';\n endInput.className = 'forms-expert-input';\n endInput.name = `${field.name}.end`;\n endInput.value = range.end || '';\n rangeContainer.appendChild(startInput);\n rangeContainer.appendChild(endInput);\n wrapper.appendChild(rangeContainer);\n if (error) {\n const errorEl = document.createElement('div');\n errorEl.className = 'forms-expert-error-message';\n errorEl.textContent = error;\n wrapper.appendChild(errorEl);\n }\n group.appendChild(wrapper);\n return group;\n }\n\n case 'address': {\n const addrContainer = document.createElement('div');\n addrContainer.className = 'forms-expert-address';\n const addrFields = field.addressFields || ['street', 'city', 'state', 'zip', 'country'];\n const addr = (value as Record<string, string>) || {};\n const labels: Record<string, string> = { street: 'Street', street2: 'Street Line 2', city: 'City', state: 'State', zip: 'ZIP', country: 'Country' };\n addrFields.forEach((af) => {\n const inp = document.createElement('input');\n inp.type = 'text';\n inp.className = 'forms-expert-input';\n inp.name = `${field.name}.${af}`;\n inp.placeholder = labels[af] || af;\n inp.value = addr[af] || '';\n addrContainer.appendChild(inp);\n });\n wrapper.appendChild(addrContainer);\n if (error) {\n const errorEl = document.createElement('div');\n errorEl.className = 'forms-expert-error-message';\n errorEl.textContent = error;\n wrapper.appendChild(errorEl);\n }\n group.appendChild(wrapper);\n return group;\n }\n\n case 'name': {\n const nameContainer = document.createElement('div');\n nameContainer.className = 'forms-expert-name';\n const nameFields = field.nameFields || ['first', 'last'];\n const nameVal = (value as Record<string, string>) || {};\n const labels: Record<string, string> = { prefix: 'Prefix', first: 'First Name', middle: 'Middle', last: 'Last Name', suffix: 'Suffix' };\n nameFields.forEach((nf) => {\n const inp = document.createElement('input');\n inp.type = 'text';\n inp.className = 'forms-expert-input';\n inp.name = `${field.name}.${nf}`;\n inp.placeholder = labels[nf] || nf;\n inp.value = nameVal[nf] || '';\n nameContainer.appendChild(inp);\n });\n wrapper.appendChild(nameContainer);\n if (error) {\n const errorEl = document.createElement('div');\n errorEl.className = 'forms-expert-error-message';\n errorEl.textContent = error;\n wrapper.appendChild(errorEl);\n }\n group.appendChild(wrapper);\n return group;\n }\n\n case 'imageChoice': {\n const imgContainer = document.createElement('div');\n imgContainer.className = 'forms-expert-image-choice';\n const opts = (field.options || []) as Array<{ label: string; value: string; imageUrl?: string }>;\n const selected = value as string;\n opts.forEach((opt) => {\n const card = document.createElement('button');\n card.type = 'button';\n card.className = `forms-expert-image-choice-item ${selected === opt.value ? 'active' : ''}`;\n card.dataset.value = opt.value;\n if (opt.imageUrl) {\n const img = document.createElement('img');\n img.src = opt.imageUrl;\n img.alt = opt.label;\n card.appendChild(img);\n }\n const lbl = document.createElement('span');\n lbl.textContent = opt.label;\n card.appendChild(lbl);\n imgContainer.appendChild(card);\n });\n wrapper.appendChild(imgContainer);\n if (error) {\n const errorEl = document.createElement('div');\n errorEl.className = 'forms-expert-error-message';\n errorEl.textContent = error;\n wrapper.appendChild(errorEl);\n }\n group.appendChild(wrapper);\n return group;\n }\n\n case 'ranking': {\n const rankContainer = document.createElement('div');\n rankContainer.className = 'forms-expert-ranking';\n const opts = (field.options || []) as string[];\n const ranked = (value as string[]) || [...opts];\n ranked.forEach((item, i) => {\n const row = document.createElement('div');\n row.className = 'forms-expert-ranking-item';\n row.textContent = `${i + 1}. ${item}`;\n row.dataset.value = item;\n rankContainer.appendChild(row);\n });\n wrapper.appendChild(rankContainer);\n if (error) {\n const errorEl = document.createElement('div');\n errorEl.className = 'forms-expert-error-message';\n errorEl.textContent = error;\n wrapper.appendChild(errorEl);\n }\n group.appendChild(wrapper);\n return group;\n }\n\n case 'location': {\n const locContainer = document.createElement('div');\n locContainer.className = 'forms-expert-location';\n const loc = (value as { lat?: number; lng?: number; address?: string }) || {};\n const addrInput = document.createElement('input');\n addrInput.type = 'text';\n addrInput.className = 'forms-expert-input';\n addrInput.name = `${field.name}.address`;\n addrInput.placeholder = 'Address';\n addrInput.value = loc.address || '';\n locContainer.appendChild(addrInput);\n const coordRow = document.createElement('div');\n coordRow.className = 'forms-expert-location-coords';\n const latInput = document.createElement('input');\n latInput.type = 'number';\n latInput.className = 'forms-expert-input';\n latInput.name = `${field.name}.lat`;\n latInput.placeholder = 'Latitude';\n latInput.step = 'any';\n latInput.value = loc.lat !== undefined ? String(loc.lat) : '';\n const lngInput = document.createElement('input');\n lngInput.type = 'number';\n lngInput.className = 'forms-expert-input';\n lngInput.name = `${field.name}.lng`;\n lngInput.placeholder = 'Longitude';\n lngInput.step = 'any';\n lngInput.value = loc.lng !== undefined ? String(loc.lng) : '';\n coordRow.appendChild(latInput);\n coordRow.appendChild(lngInput);\n locContainer.appendChild(coordRow);\n wrapper.appendChild(locContainer);\n if (error) {\n const errorEl = document.createElement('div');\n errorEl.className = 'forms-expert-error-message';\n errorEl.textContent = error;\n wrapper.appendChild(errorEl);\n }\n group.appendChild(wrapper);\n return group;\n }\n\n default:\n input = document.createElement('input');\n input.type = field.type === 'email' ? 'email' : field.type === 'number' ? 'number' : field.type === 'date' ? 'date' : 'text';\n input.className = 'forms-expert-input';\n input.value = String(value || '');\n if (field.type === 'number') {\n if (field.min !== undefined) input.min = String(field.min);\n if (field.max !== undefined) input.max = String(field.max);\n if (field.step !== undefined) input.step = String(field.step);\n }\n break;\n }\n \n input!.id = `mira-field-${field.name}`;\n input!.name = field.name;\n if (field.placeholder && 'placeholder' in input!) {\n (input as HTMLInputElement | HTMLTextAreaElement).placeholder = field.placeholder;\n }\n if (field.required) input!.required = true;\n \n if (error) {\n input!.classList.add('forms-expert-error');\n }\n \n wrapper.appendChild(input!);\n \n // Error message\n if (error) {\n const errorEl = document.createElement('div');\n errorEl.className = 'forms-expert-error-message';\n errorEl.textContent = error;\n wrapper.appendChild(errorEl);\n }\n \n group.appendChild(wrapper);\n \n return group;\n}\n\n/**\n * Render the entire form\n */\nexport function renderForm(\n schema: FormSchema,\n values: Record<string, unknown> = {},\n errors: Record<string, string> = {},\n options: {\n honeypot?: boolean;\n showBranding?: boolean;\n brandingText?: string;\n brandingUrl?: string;\n submitText?: string;\n isLoading?: boolean;\n } = {}\n): HTMLFormElement {\n const form = document.createElement('form');\n form.className = 'forms-expert';\n \n // Render fields\n schema.fields.forEach((field) => {\n const fieldEl = renderField(field, values[field.name], errors[field.name]);\n form.appendChild(fieldEl);\n });\n \n // Honeypot\n if (options.honeypot) {\n const honeypot = document.createElement('input');\n honeypot.type = 'text';\n honeypot.name = '_hp';\n honeypot.className = 'forms-expert-honeypot';\n honeypot.tabIndex = -1;\n honeypot.autocomplete = 'off';\n form.appendChild(honeypot);\n }\n \n // Hidden page URL\n const pageUrl = document.createElement('input');\n pageUrl.type = 'hidden';\n pageUrl.name = 'pageUrl';\n pageUrl.value = typeof window !== 'undefined' ? window.location.href : '';\n form.appendChild(pageUrl);\n \n // Submit button\n const button = document.createElement('button');\n button.type = 'submit';\n button.className = 'forms-expert-button';\n button.disabled = options.isLoading || false;\n \n if (options.isLoading) {\n button.innerHTML = `\n <span class=\"forms-expert-button-loading\">\n <span class=\"forms-expert-spinner\"></span>\n Submitting...\n </span>\n `;\n } else {\n button.textContent = options.submitText || 'Submit';\n }\n \n form.appendChild(button);\n \n // Branding\n if (options.showBranding !== false) {\n const brandingText = options.brandingText || 'Powered by Forms Expert';\n const brandingUrl = options.brandingUrl || 'https://mira.io';\n const branding = document.createElement('div');\n branding.className = 'forms-expert-branding';\n branding.innerHTML = `<a href=\"${brandingUrl}\" target=\"_blank\" rel=\"noopener\">${brandingText}</a>`;\n form.appendChild(branding);\n }\n \n return form;\n}\n\n/**\n * Render success message\n */\nexport function renderSuccess(message: string): HTMLElement {\n const container = document.createElement('div');\n container.className = 'forms-expert-success';\n \n container.innerHTML = `\n <svg class=\"forms-expert-success-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 13l4 4L19 7\"></path>\n </svg>\n <div class=\"forms-expert-success-message\">${escapeHtml(message)}</div>\n `;\n \n return container;\n}\n\n/**\n * Convert validation errors array to record\n */\nexport function errorsToRecord(errors: ValidationError[]): Record<string, string> {\n return errors.reduce(\n (acc, err) => ({ ...acc, [err.field]: err.message }),\n {}\n );\n}\n","/**\n * Form field types\n */\nexport type BasicFieldType =\n | 'text' | 'email' | 'number' | 'textarea' | 'select'\n | 'checkbox' | 'file' | 'date' | 'hidden';\n\nexport type InteractiveFieldType =\n | 'radio' | 'multiselect' | 'rating' | 'scale' | 'toggle'\n | 'ranking' | 'imageChoice' | 'phone' | 'url' | 'password'\n | 'richText' | 'slider' | 'currency' | 'time' | 'datetime'\n | 'dateRange' | 'address' | 'name' | 'dropdown' | 'colorPicker'\n | 'location' | 'opinionScale' | 'consent';\n\nexport type LayoutFieldType = 'heading' | 'divider' | 'paragraph';\n\nexport type FormFieldType = BasicFieldType | InteractiveFieldType | LayoutFieldType;\n\nexport interface FormFieldOption {\n label: string;\n value: string;\n imageUrl?: string;\n}\n\n/**\n * Form field definition\n */\nexport interface FormField {\n name: string;\n type: FormFieldType;\n label?: string;\n placeholder?: string;\n required?: boolean;\n options?: string[] | FormFieldOption[];\n defaultValue?: unknown;\n maxFileSize?: number;\n allowedMimeTypes?: string[];\n multiple?: boolean;\n min?: number;\n max?: number;\n step?: number;\n ratingMax?: number;\n lowLabel?: string;\n highLabel?: string;\n defaultCountryCode?: string;\n currencyCode?: string;\n currencySymbol?: string;\n addressFields?: ('street' | 'street2' | 'city' | 'state' | 'zip' | 'country')[];\n nameFields?: ('prefix' | 'first' | 'middle' | 'last' | 'suffix')[];\n content?: string;\n consentText?: string;\n consentUrl?: string;\n maxLength?: number;\n stepId?: string;\n visibleWhen?: {\n field: string;\n operator: 'eq' | 'neq' | 'contains' | 'gt' | 'lt';\n value: unknown;\n };\n}\n\n/**\n * Form styling configuration\n */\nexport interface FormStyling {\n theme: 'light' | 'dark' | 'system';\n primaryColor: string;\n backgroundColor: string;\n textColor: string;\n borderRadius: 'none' | 'sm' | 'md' | 'lg';\n fontSize: 'sm' | 'md' | 'lg';\n buttonStyle: 'filled' | 'outline';\n labelPosition: 'top' | 'left' | 'floating';\n customCss?: string;\n}\n\n/**\n * Form schema\n */\nexport interface FormSchema {\n fields: FormField[];\n styling?: FormStyling;\n}\n\n/**\n * Form captcha settings\n */\nexport interface CaptchaSettings {\n enabled: boolean;\n provider?: 'turnstile' | 'recaptcha' | 'hcaptcha';\n siteKey?: string;\n}\n\n/**\n * Form branding configuration\n */\nexport interface FormBranding {\n enabled: boolean;\n text?: string;\n url?: string;\n}\n\n/**\n * Form status response from API\n */\nexport interface FormStatusResponse {\n active: boolean;\n formId?: string;\n name?: string;\n mode?: 'free' | 'schema';\n schema?: FormSchema;\n error?: string;\n settings?: {\n captcha: CaptchaSettings;\n honeypot: boolean;\n allowAttachments: boolean;\n maxAttachments?: number;\n maxAttachmentSize?: number;\n successMessage?: string;\n redirectUrl?: string;\n };\n /** Branding configuration */\n branding?: FormBranding;\n}\n\n/**\n * Validation error\n */\nexport interface ValidationError {\n field: string;\n message: string;\n}\n\n/**\n * Validation response from API\n */\nexport interface ValidationResponse {\n valid: boolean;\n errors: ValidationError[];\n}\n\n/**\n * Submission response from API\n */\nexport interface SubmissionResponse {\n success: boolean;\n submissionId: string;\n message: string;\n}\n\n/**\n * Submit options\n */\nexport interface SubmitOptions {\n pageUrl?: string;\n captchaToken?: string;\n /** Callback for upload progress */\n onProgress?: (progress: UploadProgress) => void;\n}\n\n/**\n * Upload progress information\n */\nexport interface UploadProgress {\n loaded: number;\n total: number;\n percentage: number;\n}\n\n/**\n * File validation error\n */\nexport interface FileValidationError {\n field: string;\n file: string;\n error: 'size' | 'type' | 'count';\n message: string;\n}\n\n/**\n * SDK configuration\n */\nexport interface FormsSDKConfig {\n apiKey: string;\n resourceId: string;\n baseUrl?: string;\n}\n\n/**\n * Form handler options\n */\nexport interface FormHandlerOptions {\n /** Track form views for analytics (completion rate) */\n trackViews?: boolean;\n onSubmitStart?: () => void;\n onSubmitSuccess?: (response: SubmissionResponse) => void;\n onSubmitError?: (error: FormsError) => void;\n onValidationError?: (errors: ValidationError[]) => void;\n}\n\n/**\n * Forms SDK error\n */\nexport class FormsError extends Error {\n constructor(\n message: string,\n public code: string,\n public statusCode: number,\n public retryAfter?: number\n ) {\n super(message);\n this.name = 'FormsError';\n }\n}\n\n/**\n * Validation error class\n */\nexport class FormValidationError extends Error {\n constructor(public errors: ValidationError[]) {\n super('Validation failed');\n this.name = 'FormValidationError';\n }\n}\n","import {\n FormsSDKConfig,\n FormStatusResponse,\n ValidationResponse,\n SubmissionResponse,\n SubmitOptions,\n FormsError,\n UploadProgress,\n} from './types';\n\n/**\n * API client for forms backend\n */\nexport class FormsApiClient {\n private baseUrl: string;\n private apiKey: string;\n private resourceId: string;\n\n constructor(config: FormsSDKConfig) {\n this.apiKey = config.apiKey;\n this.resourceId = config.resourceId;\n this.baseUrl = (config.baseUrl || 'https://api.formsapp.io/api/v1').replace(/\\/$/, '');\n }\n\n /**\n * Build URL with token query parameter\n */\n private buildUrl(path: string): string {\n const separator = path.includes('?') ? '&' : '?';\n return `${this.baseUrl}${path}${separator}token=${encodeURIComponent(this.apiKey)}`;\n }\n\n /**\n * Make an API request\n */\n private async request<T>(\n method: string,\n path: string,\n body?: object\n ): Promise<T> {\n const url = this.buildUrl(path);\n\n const response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n\n const data = await response.json();\n\n if (!response.ok) {\n throw new FormsError(\n data.message || 'Request failed',\n data.code || 'UNKNOWN_ERROR',\n response.status,\n data.retryAfter\n );\n }\n\n return data;\n }\n\n /**\n * Check if form is active and get configuration\n */\n async isActive(slug: string, lang?: string): Promise<FormStatusResponse> {\n const langParam = lang ? `?lang=${encodeURIComponent(lang)}` : '';\n return this.request('GET', `/f/${this.resourceId}/${slug}/is-active${langParam}`);\n }\n\n /**\n * Validate form data without submitting\n */\n async validate(\n slug: string,\n data: Record<string, unknown>\n ): Promise<ValidationResponse> {\n return this.request('POST', `/f/${this.resourceId}/${slug}/validate`, {\n data,\n });\n }\n\n /**\n * Submit form data (supports files)\n */\n async submit(\n slug: string,\n data: Record<string, unknown>,\n options?: SubmitOptions\n ): Promise<SubmissionResponse> {\n const url = this.buildUrl(`/f/${this.resourceId}/${slug}`);\n \n // Check if data contains files\n const hasFiles = Object.values(data).some(\n (v) => v instanceof File || (v instanceof FileList && v.length > 0)\n );\n\n if (hasFiles || options?.onProgress) {\n // Use FormData and XMLHttpRequest for file uploads with progress\n return this.submitWithFormData(url, data, options);\n }\n\n // Use regular JSON request for non-file submissions\n return this.request('POST', `/f/${this.resourceId}/${slug}`, {\n data,\n pageUrl: options?.pageUrl || (typeof window !== 'undefined' ? window.location.href : undefined),\n captchaToken: options?.captchaToken,\n });\n }\n\n /**\n * Submit with FormData (for file uploads with progress tracking)\n */\n private submitWithFormData(\n url: string,\n data: Record<string, unknown>,\n options?: SubmitOptions\n ): Promise<SubmissionResponse> {\n return new Promise((resolve, reject) => {\n const formData = new FormData();\n\n // Add data fields\n for (const [key, value] of Object.entries(data)) {\n if (value instanceof File) {\n formData.append(key, value);\n } else if (value instanceof FileList) {\n Array.from(value).forEach((file) => formData.append(key, file));\n } else if (value !== undefined && value !== null) {\n formData.append(`data[${key}]`, String(value));\n }\n }\n\n // Add metadata\n const pageUrl = options?.pageUrl || (typeof window !== 'undefined' ? window.location.href : '');\n if (pageUrl) {\n formData.append('pageUrl', pageUrl);\n }\n if (options?.captchaToken) {\n formData.append('captchaToken', options.captchaToken);\n }\n\n const xhr = new XMLHttpRequest();\n\n // Progress tracking\n if (options?.onProgress) {\n xhr.upload.addEventListener('progress', (event) => {\n if (event.lengthComputable) {\n options.onProgress!({\n loaded: event.loaded,\n total: event.total,\n percentage: Math.round((event.loaded / event.total) * 100),\n });\n }\n });\n }\n\n xhr.addEventListener('load', () => {\n try {\n const response = JSON.parse(xhr.responseText);\n if (xhr.status >= 200 && xhr.status < 300) {\n resolve(response);\n } else {\n reject(new FormsError(\n response.message || 'Submission failed',\n response.code || 'UNKNOWN_ERROR',\n xhr.status,\n response.retryAfter\n ));\n }\n } catch {\n reject(new FormsError('Invalid response', 'PARSE_ERROR', xhr.status));\n }\n });\n\n xhr.addEventListener('error', () => {\n reject(new FormsError('Network error', 'NETWORK_ERROR', 0));\n });\n\n xhr.addEventListener('abort', () => {\n reject(new FormsError('Request aborted', 'ABORTED', 0));\n });\n\n xhr.open('POST', url);\n xhr.send(formData);\n });\n }\n\n /**\n * Track a form view (for analytics completion rate)\n */\n async trackView(slug: string): Promise<void> {\n const url = this.buildUrl(`/f/${this.resourceId}/${slug}/view`);\n await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' } }).catch(() => {});\n }\n\n /**\n * Get resource ID\n */\n getResourceId(): string {\n return this.resourceId;\n }\n\n /**\n * Get base URL\n */\n getBaseUrl(): string {\n return this.baseUrl;\n }\n}\n","import { FormsApiClient } from './api-client';\nimport {\n FormsSDKConfig,\n FormStatusResponse,\n ValidationResponse,\n SubmissionResponse,\n SubmitOptions,\n FormHandlerOptions,\n FormsError,\n FormValidationError,\n} from './types';\n\n/**\n * Form handler for a specific form\n */\nexport class FormHandler {\n private apiClient: FormsApiClient;\n private slug: string;\n private config: FormStatusResponse | null = null;\n private options: FormHandlerOptions;\n\n constructor(\n apiClient: FormsApiClient,\n slug: string,\n options: FormHandlerOptions = {}\n ) {\n this.apiClient = apiClient;\n this.slug = slug;\n this.options = options;\n }\n\n /**\n * Initialize form handler and fetch configuration\n */\n async initialize(lang?: string): Promise<FormStatusResponse> {\n this.config = await this.apiClient.isActive(this.slug, lang);\n if (this.options.trackViews) {\n this.apiClient.trackView(this.slug);\n }\n return this.config;\n }\n\n /**\n * Get cached form configuration\n */\n getConfig(): FormStatusResponse | null {\n return this.config;\n }\n\n /**\n * Check if form is active\n */\n isActive(): boolean {\n return this.config?.active ?? false;\n }\n\n /**\n * Check if captcha is required\n */\n requiresCaptcha(): boolean {\n return this.config?.settings?.captcha?.enabled ?? false;\n }\n\n /**\n * Get captcha provider\n */\n getCaptchaProvider(): 'turnstile' | 'recaptcha' | 'hcaptcha' | undefined {\n return this.config?.settings?.captcha?.provider;\n }\n\n /**\n * Get form schema\n */\n getSchema() {\n return this.config?.schema;\n }\n\n /**\n * Validate form data\n */\n async validate(data: Record<string, unknown>): Promise<ValidationResponse> {\n return this.apiClient.validate(this.slug, data);\n }\n\n /**\n * Submit form data\n */\n async submit(\n data: Record<string, unknown>,\n options?: SubmitOptions\n ): Promise<SubmissionResponse> {\n this.options.onSubmitStart?.();\n\n try {\n // Validate first if in schema mode\n if (this.config?.mode === 'schema') {\n const validation = await this.validate(data);\n if (!validation.valid) {\n this.options.onValidationError?.(validation.errors);\n throw new FormValidationError(validation.errors);\n }\n }\n\n const response = await this.apiClient.submit(this.slug, data, options);\n this.options.onSubmitSuccess?.(response);\n return response;\n } catch (error) {\n if (error instanceof FormsError) {\n this.options.onSubmitError?.(error);\n }\n throw error;\n }\n }\n\n /**\n * Get success message from config\n */\n getSuccessMessage(): string {\n return this.config?.settings?.successMessage || 'Form submitted successfully!';\n }\n\n /**\n * Get redirect URL from config\n */\n getRedirectUrl(): string | undefined {\n return this.config?.settings?.redirectUrl;\n }\n}\n\n/**\n * Main Forms SDK class\n */\nexport class FormsSDK {\n private apiClient: FormsApiClient;\n\n constructor(config: FormsSDKConfig) {\n this.apiClient = new FormsApiClient(config);\n }\n\n /**\n * Check if form is active and get configuration\n */\n async isActive(slug: string, lang?: string): Promise<FormStatusResponse> {\n return this.apiClient.isActive(slug, lang);\n }\n\n /**\n * Validate form data without submitting\n */\n async validate(\n slug: string,\n data: Record<string, unknown>\n ): Promise<ValidationResponse> {\n return this.apiClient.validate(slug, data);\n }\n\n /**\n * Submit form data\n */\n async submit(\n slug: string,\n data: Record<string, unknown>,\n options?: SubmitOptions\n ): Promise<SubmissionResponse> {\n return this.apiClient.submit(slug, data, options);\n }\n\n /**\n * Create a form handler for a specific form\n */\n form(slug: string, options?: FormHandlerOptions): FormHandler {\n return new FormHandler(this.apiClient, slug, options);\n }\n\n /**\n * Track a form view (for analytics completion rate)\n */\n async trackView(slug: string): Promise<void> {\n return this.apiClient.trackView(slug);\n }\n\n /**\n * Submit with retry logic for rate limits\n */\n async submitWithRetry(\n slug: string,\n data: Record<string, unknown>,\n options?: SubmitOptions & { maxRetries?: number }\n ): Promise<SubmissionResponse> {\n const maxRetries = options?.maxRetries ?? 3;\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n try {\n return await this.submit(slug, data, options);\n } catch (error) {\n lastError = error as Error;\n\n if (error instanceof FormsError) {\n // Don't retry validation or auth errors\n if (\n [\n 'VALIDATION_ERROR',\n 'CAPTCHA_REQUIRED',\n 'ORIGIN_NOT_ALLOWED',\n ].includes(error.code)\n ) {\n throw error;\n }\n\n // Retry rate limits with backoff\n if (error.code.includes('RATE_LIMIT')) {\n const retryAfter = error.retryAfter || Math.pow(2, attempt) * 1000;\n await new Promise((resolve) => setTimeout(resolve, retryAfter));\n continue;\n }\n }\n\n // Exponential backoff for network errors\n await new Promise((resolve) =>\n setTimeout(resolve, Math.pow(2, attempt) * 1000)\n );\n }\n }\n\n throw lastError!;\n }\n}\n","import {\n FormsSDK,\n FormsSDKConfig,\n FormStatusResponse,\n SubmissionResponse,\n ValidationError,\n FormValidationError,\n FormsError,\n} from '../core';\nimport { generateFormStyles } from './styles';\nimport { renderForm, renderSuccess, errorsToRecord } from './renderer';\n\nexport interface FormWidgetOptions {\n /** Target element or selector */\n target: string | HTMLElement;\n /** Form slug */\n slug: string;\n /** Track form views for analytics (completion rate). Default: false */\n trackViews?: boolean;\n /** Custom submit button text */\n submitText?: string;\n /** Success callback */\n onSuccess?: (response: SubmissionResponse) => void;\n /** Error callback */\n onError?: (error: Error) => void;\n /** Validation error callback */\n onValidationError?: (errors: ValidationError[]) => void;\n /** Reset form after submission */\n resetOnSuccess?: boolean;\n /** Redirect after success (overrides form config) */\n redirectUrl?: string;\n /** Language code to pass to backend */\n lang?: string;\n}\n\n/**\n * Form widget for embedding forms\n */\nexport class FormWidget {\n private sdk: FormsSDK;\n private container: HTMLElement;\n private config: FormStatusResponse | null = null;\n private values: Record<string, unknown> = {};\n private errors: Record<string, string> = {};\n private isLoading = false;\n private isSubmitted = false;\n private options: FormWidgetOptions;\n private styleEl: HTMLStyleElement | null = null;\n\n constructor(sdkConfig: FormsSDKConfig, options: FormWidgetOptions) {\n this.sdk = new FormsSDK(sdkConfig);\n this.options = options;\n\n // Get container element\n const target = options.target;\n if (typeof target === 'string') {\n const el = document.querySelector(target);\n if (!el) throw new Error(`Element not found: ${target}`);\n this.container = el as HTMLElement;\n } else {\n this.container = target;\n }\n }\n\n /**\n * Initialize and render the form\n */\n async init(): Promise<void> {\n try {\n this.config = await this.sdk.isActive(this.options.slug, this.options.lang);\n\n if (!this.config.active) {\n this.renderError('This form is not available');\n return;\n }\n\n if (this.options.trackViews) {\n this.sdk.trackView(this.options.slug);\n }\n\n this.injectStyles();\n this.render();\n } catch (error) {\n this.renderError('Failed to load form');\n this.options.onError?.(error as Error);\n }\n }\n\n /**\n * Inject CSS styles\n */\n private injectStyles(): void {\n if (this.styleEl) return;\n\n this.styleEl = document.createElement('style');\n this.styleEl.id = `forms-expert-styles-${this.options.slug}`;\n this.styleEl.textContent = generateFormStyles(this.config?.schema?.styling);\n document.head.appendChild(this.styleEl);\n }\n\n /**\n * Render the form\n */\n private render(): void {\n if (!this.config?.schema) return;\n\n if (this.isSubmitted) {\n this.container.innerHTML = '';\n const successMessage =\n this.config.settings?.successMessage || 'Form submitted successfully!';\n this.container.appendChild(renderSuccess(successMessage));\n return;\n }\n\n const form = renderForm(this.config.schema, this.values, this.errors, {\n honeypot: this.config.settings?.honeypot,\n showBranding: this.config.branding?.enabled !== false,\n brandingText: this.config.branding?.text,\n brandingUrl: this.config.branding?.url,\n submitText: this.options.submitText,\n isLoading: this.isLoading,\n });\n\n // Handle input changes\n form.addEventListener('input', (e) => {\n const target = e.target as HTMLInputElement;\n if (target.name && target.name !== '_hp' && target.name !== 'pageUrl') {\n if (target.type === 'checkbox') {\n this.values[target.name] = target.checked;\n } else if (target.type === 'file') {\n // Store FileList for multiple files, single File for single uploads\n this.values[target.name] = target.multiple ? target.files : target.files?.[0];\n } else {\n this.values[target.name] = target.value;\n }\n // Clear error on change\n if (this.errors[target.name]) {\n delete this.errors[target.name];\n this.render();\n }\n }\n });\n\n // Handle submit\n form.addEventListener('submit', (e) => {\n e.preventDefault();\n this.handleSubmit();\n });\n\n this.container.innerHTML = '';\n this.container.appendChild(form);\n }\n\n /**\n * Handle form submission\n */\n private async handleSubmit(): Promise<void> {\n if (this.isLoading || !this.config) return;\n\n this.isLoading = true;\n this.errors = {};\n this.render();\n\n try {\n const response = await this.sdk.submit(this.options.slug, this.values);\n\n this.isLoading = false;\n this.isSubmitted = true;\n this.render();\n\n this.options.onSuccess?.(response);\n\n // Handle redirect\n const redirectUrl =\n this.options.redirectUrl || this.config.settings?.redirectUrl;\n if (redirectUrl) {\n setTimeout(() => {\n window.location.href = redirectUrl;\n }, 1500);\n }\n\n // Reset form after delay if configured\n if (this.options.resetOnSuccess) {\n setTimeout(() => {\n this.reset();\n }, 3000);\n }\n } catch (error) {\n this.isLoading = false;\n\n if (error instanceof FormValidationError) {\n this.errors = errorsToRecord(error.errors);\n this.options.onValidationError?.(error.errors);\n } else {\n this.options.onError?.(error as Error);\n }\n\n this.render();\n }\n }\n\n /**\n * Reset form to initial state\n */\n reset(): void {\n this.values = {};\n this.errors = {};\n this.isLoading = false;\n this.isSubmitted = false;\n this.render();\n }\n\n /**\n * Render error message\n */\n private renderError(message: string): void {\n this.container.innerHTML = `\n <div class=\"forms-expert\" style=\"text-align: center; padding: 2rem; color: #ef4444;\">\n <p>${message}</p>\n </div>\n `;\n }\n\n /**\n * Destroy widget\n */\n destroy(): void {\n this.container.innerHTML = '';\n this.styleEl?.remove();\n this.styleEl = null;\n }\n}\n\n/**\n * Auto-initialize forms on page load\n */\nexport function autoInit(): void {\n const forms = document.querySelectorAll('[data-forms-expert]');\n\n forms.forEach((el) => {\n const apiKey = el.getAttribute('data-api-key');\n const resourceId = el.getAttribute('data-resource-id');\n const slug = el.getAttribute('data-forms-expert');\n const baseUrl = el.getAttribute('data-base-url') || undefined;\n\n if (!apiKey || !resourceId || !slug) {\n console.error('Forms Expert: Missing required attributes', {\n apiKey: !!apiKey,\n resourceId: !!resourceId,\n slug: !!slug,\n });\n return;\n }\n\n const widget = new FormWidget(\n { apiKey, resourceId, baseUrl },\n {\n target: el as HTMLElement,\n slug,\n trackViews: el.getAttribute('data-track-views') === 'true',\n submitText: el.getAttribute('data-submit-text') || undefined,\n resetOnSuccess: el.getAttribute('data-reset') === 'true',\n lang: el.getAttribute('data-lang') || undefined,\n }\n );\n\n widget.init();\n });\n}\n\n// Auto-init on DOMContentLoaded\nif (typeof window !== 'undefined') {\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', autoInit);\n } else {\n autoInit();\n }\n}\n"],"mappings":"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","names":["defaultStyling","getBorderRadius","radius","getFontSize","size","generateFormStyles","styling","s","fontSize","escapeHtml","text","div","renderField","field","value","error","group","h","p","hr","input","label","text","escapeHtml","link","errorEl","wrapper","defaultOption","opt","option","radioGroup","radioWrapper","radioInput","checkGroup","selected","checkWrapper","checkInput","ratingContainer","max","current","star","scaleContainer","min","i","btn","labels","rangeContainer","range","startInput","endInput","addrContainer","addrFields","addr","af","inp","nameContainer","nameFields","nameVal","nf","imgContainer","opts","card","img","lbl","rankContainer","item","row","locContainer","loc","addrInput","coordRow","latInput","lngInput","renderForm","schema","values","errors","options","form","fieldEl","honeypot","pageUrl","button","brandingText","brandingUrl","branding","renderSuccess","message","container","errorsToRecord","acc","err","FormsError","message","code","statusCode","retryAfter","FormValidationError","errors","FormsApiClient","config","path","separator","method","body","url","response","data","FormsError","slug","lang","langParam","options","v","resolve","reject","formData","key","value","file","pageUrl","xhr","event","FormHandler","apiClient","slug","options","lang","data","validation","FormValidationError","response","error","FormsError","FormsSDK","config","FormsApiClient","maxRetries","lastError","attempt","retryAfter","resolve","FormWidget","sdkConfig","options","FormsSDK","target","el","error","generateFormStyles","successMessage","renderSuccess","form","renderForm","e","response","redirectUrl","FormValidationError","errorsToRecord","message","autoInit","apiKey","resourceId","slug","baseUrl"]}
|
|
1
|
+
{"version":3,"sources":["../../vanilla/styles.ts","../../vanilla/renderer.ts","../../core/types.ts","../../core/api-client.ts","../../core/forms-sdk.ts","../../vanilla/widget.ts"],"sourcesContent":["import { FormStyling } from '../core/types';\n\nconst defaultStyling: FormStyling = {\n theme: 'light',\n primaryColor: '#3b82f6',\n backgroundColor: '#ffffff',\n textColor: '#1f2937',\n borderRadius: 'md',\n fontSize: 'md',\n buttonStyle: 'filled',\n labelPosition: 'top',\n};\n\nfunction getBorderRadius(radius: FormStyling['borderRadius']): string {\n switch (radius) {\n case 'none': return '0';\n case 'sm': return '0.125rem';\n case 'md': return '0.375rem';\n case 'lg': return '0.5rem';\n default: return '0.375rem';\n }\n}\n\nfunction getFontSize(size: FormStyling['fontSize']): string {\n switch (size) {\n case 'sm': return '0.875rem';\n case 'md': return '1rem';\n case 'lg': return '1.125rem';\n default: return '1rem';\n }\n}\n\nexport function generateFormStyles(styling: FormStyling = defaultStyling): string {\n const s = { ...defaultStyling, ...styling };\n const radius = getBorderRadius(s.borderRadius);\n const fontSize = getFontSize(s.fontSize);\n\n return `\n.forms-expert {\n font-family: system-ui, -apple-system, sans-serif;\n font-size: ${fontSize};\n color: ${s.textColor};\n background-color: ${s.backgroundColor};\n padding: 1.5rem;\n border-radius: ${radius};\n box-sizing: border-box;\n}\n\n.forms-expert * {\n box-sizing: border-box;\n}\n\n.forms-expert-group {\n margin-bottom: 1rem;\n ${s.labelPosition === 'left' ? 'display: flex; align-items: flex-start; gap: 1rem;' : ''}\n}\n\n.forms-expert-label {\n display: block;\n font-weight: 500;\n color: ${s.textColor};\n ${s.labelPosition === 'left' ? 'width: 33%; flex-shrink: 0; padding-top: 0.5rem;' : 'margin-bottom: 0.25rem;'}\n}\n\n.forms-expert-required {\n color: #ef4444;\n margin-left: 0.25rem;\n}\n\n.forms-expert-input-wrapper {\n ${s.labelPosition === 'left' ? 'flex: 1;' : ''}\n}\n\n.forms-expert-input,\n.forms-expert-textarea,\n.forms-expert-select {\n width: 100%;\n padding: 0.5rem 0.75rem;\n border: 1px solid ${s.theme === 'dark' ? '#4b5563' : '#d1d5db'};\n border-radius: ${radius};\n font-size: ${fontSize};\n font-family: inherit;\n background-color: ${s.theme === 'dark' ? '#374151' : '#ffffff'};\n color: ${s.textColor};\n transition: border-color 0.15s, box-shadow 0.15s;\n}\n\n.forms-expert-input:focus,\n.forms-expert-textarea:focus,\n.forms-expert-select:focus {\n outline: none;\n border-color: ${s.primaryColor};\n box-shadow: 0 0 0 2px ${s.primaryColor}33;\n}\n\n.forms-expert-input.forms-expert-error,\n.forms-expert-textarea.forms-expert-error,\n.forms-expert-select.forms-expert-error {\n border-color: #ef4444;\n}\n\n.forms-expert-textarea {\n min-height: 100px;\n resize: vertical;\n}\n\n.forms-expert-checkbox-group {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n margin-bottom: 1rem;\n}\n\n.forms-expert-checkbox {\n width: 1rem;\n height: 1rem;\n accent-color: ${s.primaryColor};\n cursor: pointer;\n}\n\n.forms-expert-file-wrapper {\n position: relative;\n}\n\n.forms-expert-file {\n width: 100%;\n padding: 0.5rem 0.75rem;\n border: 1px solid ${s.theme === 'dark' ? '#4b5563' : '#d1d5db'};\n border-radius: ${radius};\n font-size: ${fontSize};\n background-color: ${s.theme === 'dark' ? '#374151' : '#ffffff'};\n cursor: pointer;\n}\n\n.forms-expert-error-message {\n color: #ef4444;\n font-size: 0.875rem;\n margin-top: 0.25rem;\n}\n\n.forms-expert-button {\n width: 100%;\n padding: 0.625rem 1.25rem;\n margin-top: 1rem;\n font-weight: 500;\n font-size: ${fontSize};\n font-family: inherit;\n border-radius: ${radius};\n cursor: pointer;\n transition: opacity 0.2s, transform 0.1s;\n ${s.buttonStyle === 'filled' \n ? `background-color: ${s.primaryColor}; color: white; border: none;`\n : `background-color: transparent; color: ${s.primaryColor}; border: 2px solid ${s.primaryColor};`\n }\n}\n\n.forms-expert-button:hover {\n opacity: 0.9;\n}\n\n.forms-expert-button:active {\n transform: scale(0.98);\n}\n\n.forms-expert-button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n transform: none;\n}\n\n.forms-expert-button-loading {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n}\n\n.forms-expert-spinner {\n width: 1rem;\n height: 1rem;\n border: 2px solid transparent;\n border-top-color: currentColor;\n border-radius: 50%;\n animation: forms-expert-spin 0.6s linear infinite;\n}\n\n@keyframes forms-expert-spin {\n to { transform: rotate(360deg); }\n}\n\n.forms-expert-honeypot {\n position: absolute;\n left: -9999px;\n opacity: 0;\n}\n\n.forms-expert-success {\n text-align: center;\n padding: 2rem;\n color: ${s.textColor};\n}\n\n.forms-expert-success-icon {\n width: 3rem;\n height: 3rem;\n margin: 0 auto 1rem;\n color: #22c55e;\n}\n\n.forms-expert-success-message {\n font-size: 1.125rem;\n font-weight: 500;\n margin-bottom: 0.5rem;\n}\n\n.forms-expert-branding {\n text-align: center;\n margin-top: 1rem;\n padding-top: 0.75rem;\n border-top: 1px solid ${s.theme === 'dark' ? '#374151' : '#e5e7eb'};\n}\n\n.forms-expert-branding a {\n color: ${s.theme === 'dark' ? '#9ca3af' : '#6b7280'};\n text-decoration: none;\n font-size: 0.75rem;\n}\n\n.forms-expert-branding a:hover {\n text-decoration: underline;\n}\n\n${s.customCss || ''}\n`.trim();\n}\n","import { FormField, FormSchema, ValidationError } from '../core/types';\n\n/**\n * Escape HTML special characters\n */\nfunction escapeHtml(text: string): string {\n const div = document.createElement('div');\n div.textContent = text;\n return div.innerHTML;\n}\n\n/**\n * Layout field types that don't capture data\n */\nconst LAYOUT_TYPES = new Set(['heading', 'divider', 'paragraph']);\n\n/**\n * Create a form field element\n */\nexport function renderField(\n field: FormField,\n value: unknown,\n error?: string\n): HTMLElement {\n const group = document.createElement('div');\n\n // Layout fields\n if (field.type === 'heading') {\n group.className = 'forms-expert-group';\n const h = document.createElement('h3');\n h.className = 'forms-expert-heading';\n h.textContent = field.label || '';\n group.appendChild(h);\n if (field.content) {\n const p = document.createElement('p');\n p.className = 'forms-expert-heading-subtitle';\n p.textContent = field.content;\n group.appendChild(p);\n }\n return group;\n }\n\n if (field.type === 'divider') {\n const hr = document.createElement('hr');\n hr.className = 'forms-expert-divider';\n return hr;\n }\n\n if (field.type === 'paragraph') {\n group.className = 'forms-expert-group';\n if (field.label) {\n const p = document.createElement('p');\n p.className = 'forms-expert-paragraph-label';\n p.textContent = field.label;\n group.appendChild(p);\n }\n if (field.content) {\n const p = document.createElement('p');\n p.className = 'forms-expert-paragraph';\n p.textContent = field.content;\n group.appendChild(p);\n }\n return group;\n }\n\n // Hidden field\n if (field.type === 'hidden') {\n const input = document.createElement('input');\n input.type = 'hidden';\n input.name = field.name;\n input.value = String(field.defaultValue ?? value ?? '');\n group.appendChild(input);\n group.style.display = 'none';\n return group;\n }\n\n // Checkbox / toggle / consent\n if (field.type === 'checkbox' || field.type === 'toggle' || field.type === 'consent') {\n group.className = 'forms-expert-checkbox-group';\n \n const input = document.createElement('input');\n input.type = 'checkbox';\n input.id = `mira-field-${field.name}`;\n input.name = field.name;\n input.className = 'forms-expert-checkbox';\n input.checked = Boolean(value);\n if (field.required) input.required = true;\n \n const label = document.createElement('label');\n label.htmlFor = input.id;\n const text = field.type === 'consent' ? (field.consentText || field.label || field.name) : (field.label || field.name);\n label.innerHTML = `${escapeHtml(text)}${field.required ? '<span class=\"forms-expert-required\">*</span>' : ''}`;\n \n group.appendChild(input);\n group.appendChild(label);\n\n if (field.type === 'consent' && field.consentUrl) {\n const link = document.createElement('a');\n link.href = field.consentUrl;\n link.target = '_blank';\n link.rel = 'noopener noreferrer';\n link.textContent = 'View policy';\n link.className = 'forms-expert-consent-link';\n group.appendChild(link);\n }\n\n if (error) {\n const errorEl = document.createElement('div');\n errorEl.className = 'forms-expert-error-message';\n errorEl.textContent = error;\n group.appendChild(errorEl);\n }\n return group;\n }\n\n // All other fields\n group.className = 'forms-expert-group';\n \n // Label\n const label = document.createElement('label');\n label.className = 'forms-expert-label';\n label.htmlFor = `mira-field-${field.name}`;\n label.innerHTML = `${escapeHtml(field.label || field.name)}${field.required ? '<span class=\"forms-expert-required\">*</span>' : ''}`;\n group.appendChild(label);\n \n // Input wrapper\n const wrapper = document.createElement('div');\n wrapper.className = 'forms-expert-input-wrapper';\n \n let input: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement;\n \n switch (field.type) {\n case 'textarea':\n case 'richText':\n input = document.createElement('textarea');\n input.className = 'forms-expert-textarea';\n input.value = String(value || '');\n if (field.maxLength) input.maxLength = field.maxLength;\n break;\n \n case 'select':\n case 'dropdown': {\n input = document.createElement('select');\n input.className = 'forms-expert-select';\n \n const defaultOption = document.createElement('option');\n defaultOption.value = '';\n defaultOption.textContent = field.placeholder || 'Select an option...';\n input.appendChild(defaultOption);\n \n const opts = (field.options || []) as string[];\n opts.forEach((opt) => {\n const option = document.createElement('option');\n option.value = opt;\n option.textContent = opt;\n if (value === opt) option.selected = true;\n input.appendChild(option);\n });\n break;\n }\n\n case 'radio': {\n // Render as radio group\n const radioGroup = document.createElement('div');\n radioGroup.className = 'forms-expert-radio-group';\n const opts = (field.options || []) as string[];\n opts.forEach((opt) => {\n const radioWrapper = document.createElement('label');\n radioWrapper.className = 'forms-expert-radio-item';\n const radioInput = document.createElement('input');\n radioInput.type = 'radio';\n radioInput.name = field.name;\n radioInput.value = opt;\n radioInput.checked = value === opt;\n radioWrapper.appendChild(radioInput);\n radioWrapper.appendChild(document.createTextNode(` ${opt}`));\n radioGroup.appendChild(radioWrapper);\n });\n wrapper.appendChild(radioGroup);\n if (error) {\n const errorEl = document.createElement('div');\n errorEl.className = 'forms-expert-error-message';\n errorEl.textContent = error;\n wrapper.appendChild(errorEl);\n }\n group.appendChild(wrapper);\n return group;\n }\n\n case 'multiselect': {\n const checkGroup = document.createElement('div');\n checkGroup.className = 'forms-expert-multiselect-group';\n const selected = (value as string[]) || [];\n const opts = (field.options || []) as string[];\n opts.forEach((opt) => {\n const checkWrapper = document.createElement('label');\n checkWrapper.className = 'forms-expert-checkbox-item';\n const checkInput = document.createElement('input');\n checkInput.type = 'checkbox';\n checkInput.name = field.name;\n checkInput.value = opt;\n checkInput.checked = selected.includes(opt);\n checkWrapper.appendChild(checkInput);\n checkWrapper.appendChild(document.createTextNode(` ${opt}`));\n checkGroup.appendChild(checkWrapper);\n });\n wrapper.appendChild(checkGroup);\n if (error) {\n const errorEl = document.createElement('div');\n errorEl.className = 'forms-expert-error-message';\n errorEl.textContent = error;\n wrapper.appendChild(errorEl);\n }\n group.appendChild(wrapper);\n return group;\n }\n\n case 'rating': {\n const ratingContainer = document.createElement('div');\n ratingContainer.className = 'forms-expert-rating';\n const max = field.ratingMax || 5;\n const current = (value as number) || 0;\n for (let i = 1; i <= max; i++) {\n const star = document.createElement('button');\n star.type = 'button';\n star.className = `forms-expert-rating-star ${i <= current ? 'active' : ''}`;\n star.textContent = '★';\n star.dataset.value = String(i);\n ratingContainer.appendChild(star);\n }\n wrapper.appendChild(ratingContainer);\n if (error) {\n const errorEl = document.createElement('div');\n errorEl.className = 'forms-expert-error-message';\n errorEl.textContent = error;\n wrapper.appendChild(errorEl);\n }\n group.appendChild(wrapper);\n return group;\n }\n\n case 'scale':\n case 'opinionScale': {\n const scaleContainer = document.createElement('div');\n scaleContainer.className = 'forms-expert-scale';\n const min = field.min ?? (field.type === 'opinionScale' ? 0 : 1);\n const max = field.max ?? (field.type === 'opinionScale' ? 10 : 5);\n const current = value as number | undefined;\n for (let i = min; i <= max; i++) {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = `forms-expert-scale-btn ${current === i ? 'active' : ''}`;\n btn.textContent = String(i);\n btn.dataset.value = String(i);\n scaleContainer.appendChild(btn);\n }\n wrapper.appendChild(scaleContainer);\n if (field.lowLabel || field.highLabel) {\n const labels = document.createElement('div');\n labels.className = 'forms-expert-scale-labels';\n labels.innerHTML = `<span>${escapeHtml(field.lowLabel || '')}</span><span>${escapeHtml(field.highLabel || '')}</span>`;\n wrapper.appendChild(labels);\n }\n if (error) {\n const errorEl = document.createElement('div');\n errorEl.className = 'forms-expert-error-message';\n errorEl.textContent = error;\n wrapper.appendChild(errorEl);\n }\n group.appendChild(wrapper);\n return group;\n }\n\n case 'slider': {\n input = document.createElement('input');\n input.type = 'range';\n input.className = 'forms-expert-slider';\n input.min = String(field.min ?? 0);\n input.max = String(field.max ?? 100);\n input.step = String(field.step ?? 1);\n input.value = String(value ?? field.min ?? 0);\n break;\n }\n\n case 'file':\n input = document.createElement('input');\n input.type = 'file';\n input.className = 'forms-expert-file';\n if (field.allowedMimeTypes?.length) {\n input.accept = field.allowedMimeTypes.join(',');\n }\n if (field.multiple) {\n input.multiple = true;\n }\n break;\n\n case 'currency': {\n input = document.createElement('input');\n input.type = 'number';\n input.className = 'forms-expert-input';\n input.value = String(value ?? '');\n if (field.min !== undefined) input.min = String(field.min);\n if (field.max !== undefined) input.max = String(field.max);\n input.step = String(field.step || 0.01);\n break;\n }\n\n case 'phone':\n input = document.createElement('input');\n input.type = 'tel';\n input.className = 'forms-expert-input';\n input.value = String(value || '');\n break;\n\n case 'url':\n input = document.createElement('input');\n input.type = 'url';\n input.className = 'forms-expert-input';\n input.value = String(value || '');\n break;\n\n case 'password':\n input = document.createElement('input');\n input.type = 'password';\n input.className = 'forms-expert-input';\n input.value = String(value || '');\n break;\n\n case 'time':\n input = document.createElement('input');\n input.type = 'time';\n input.className = 'forms-expert-input';\n input.value = String(value || '');\n break;\n\n case 'datetime':\n input = document.createElement('input');\n input.type = 'datetime-local';\n input.className = 'forms-expert-input';\n input.value = String(value || '');\n break;\n\n case 'colorPicker':\n input = document.createElement('input');\n input.type = 'color';\n input.className = 'forms-expert-color';\n input.value = String(value || '#000000');\n break;\n\n case 'dateRange': {\n // Two date inputs\n const rangeContainer = document.createElement('div');\n rangeContainer.className = 'forms-expert-date-range';\n const range = (value as { start?: string; end?: string }) || {};\n const startInput = document.createElement('input');\n startInput.type = 'date';\n startInput.className = 'forms-expert-input';\n startInput.name = `${field.name}.start`;\n startInput.value = range.start || '';\n const endInput = document.createElement('input');\n endInput.type = 'date';\n endInput.className = 'forms-expert-input';\n endInput.name = `${field.name}.end`;\n endInput.value = range.end || '';\n rangeContainer.appendChild(startInput);\n rangeContainer.appendChild(endInput);\n wrapper.appendChild(rangeContainer);\n if (error) {\n const errorEl = document.createElement('div');\n errorEl.className = 'forms-expert-error-message';\n errorEl.textContent = error;\n wrapper.appendChild(errorEl);\n }\n group.appendChild(wrapper);\n return group;\n }\n\n case 'address': {\n const addrContainer = document.createElement('div');\n addrContainer.className = 'forms-expert-address';\n const addrFields = field.addressFields || ['street', 'city', 'state', 'zip', 'country'];\n const addr = (value as Record<string, string>) || {};\n const labels: Record<string, string> = { street: 'Street', street2: 'Street Line 2', city: 'City', state: 'State', zip: 'ZIP', country: 'Country' };\n addrFields.forEach((af) => {\n const inp = document.createElement('input');\n inp.type = 'text';\n inp.className = 'forms-expert-input';\n inp.name = `${field.name}.${af}`;\n inp.placeholder = labels[af] || af;\n inp.value = addr[af] || '';\n addrContainer.appendChild(inp);\n });\n wrapper.appendChild(addrContainer);\n if (error) {\n const errorEl = document.createElement('div');\n errorEl.className = 'forms-expert-error-message';\n errorEl.textContent = error;\n wrapper.appendChild(errorEl);\n }\n group.appendChild(wrapper);\n return group;\n }\n\n case 'name': {\n const nameContainer = document.createElement('div');\n nameContainer.className = 'forms-expert-name';\n const nameFields = field.nameFields || ['first', 'last'];\n const nameVal = (value as Record<string, string>) || {};\n const labels: Record<string, string> = { prefix: 'Prefix', first: 'First Name', middle: 'Middle', last: 'Last Name', suffix: 'Suffix' };\n nameFields.forEach((nf) => {\n const inp = document.createElement('input');\n inp.type = 'text';\n inp.className = 'forms-expert-input';\n inp.name = `${field.name}.${nf}`;\n inp.placeholder = labels[nf] || nf;\n inp.value = nameVal[nf] || '';\n nameContainer.appendChild(inp);\n });\n wrapper.appendChild(nameContainer);\n if (error) {\n const errorEl = document.createElement('div');\n errorEl.className = 'forms-expert-error-message';\n errorEl.textContent = error;\n wrapper.appendChild(errorEl);\n }\n group.appendChild(wrapper);\n return group;\n }\n\n case 'imageChoice': {\n const imgContainer = document.createElement('div');\n imgContainer.className = 'forms-expert-image-choice';\n const opts = (field.options || []) as Array<{ label: string; value: string; imageUrl?: string }>;\n const selected = value as string;\n opts.forEach((opt) => {\n const card = document.createElement('button');\n card.type = 'button';\n card.className = `forms-expert-image-choice-item ${selected === opt.value ? 'active' : ''}`;\n card.dataset.value = opt.value;\n if (opt.imageUrl) {\n const img = document.createElement('img');\n img.src = opt.imageUrl;\n img.alt = opt.label;\n card.appendChild(img);\n }\n const lbl = document.createElement('span');\n lbl.textContent = opt.label;\n card.appendChild(lbl);\n imgContainer.appendChild(card);\n });\n wrapper.appendChild(imgContainer);\n if (error) {\n const errorEl = document.createElement('div');\n errorEl.className = 'forms-expert-error-message';\n errorEl.textContent = error;\n wrapper.appendChild(errorEl);\n }\n group.appendChild(wrapper);\n return group;\n }\n\n case 'ranking': {\n const rankContainer = document.createElement('div');\n rankContainer.className = 'forms-expert-ranking';\n const opts = (field.options || []) as string[];\n const ranked = (value as string[]) || [...opts];\n ranked.forEach((item, i) => {\n const row = document.createElement('div');\n row.className = 'forms-expert-ranking-item';\n row.textContent = `${i + 1}. ${item}`;\n row.dataset.value = item;\n rankContainer.appendChild(row);\n });\n wrapper.appendChild(rankContainer);\n if (error) {\n const errorEl = document.createElement('div');\n errorEl.className = 'forms-expert-error-message';\n errorEl.textContent = error;\n wrapper.appendChild(errorEl);\n }\n group.appendChild(wrapper);\n return group;\n }\n\n case 'location': {\n const locContainer = document.createElement('div');\n locContainer.className = 'forms-expert-location';\n const loc = (value as { lat?: number; lng?: number; address?: string }) || {};\n const addrInput = document.createElement('input');\n addrInput.type = 'text';\n addrInput.className = 'forms-expert-input';\n addrInput.name = `${field.name}.address`;\n addrInput.placeholder = 'Address';\n addrInput.value = loc.address || '';\n locContainer.appendChild(addrInput);\n const coordRow = document.createElement('div');\n coordRow.className = 'forms-expert-location-coords';\n const latInput = document.createElement('input');\n latInput.type = 'number';\n latInput.className = 'forms-expert-input';\n latInput.name = `${field.name}.lat`;\n latInput.placeholder = 'Latitude';\n latInput.step = 'any';\n latInput.value = loc.lat !== undefined ? String(loc.lat) : '';\n const lngInput = document.createElement('input');\n lngInput.type = 'number';\n lngInput.className = 'forms-expert-input';\n lngInput.name = `${field.name}.lng`;\n lngInput.placeholder = 'Longitude';\n lngInput.step = 'any';\n lngInput.value = loc.lng !== undefined ? String(loc.lng) : '';\n coordRow.appendChild(latInput);\n coordRow.appendChild(lngInput);\n locContainer.appendChild(coordRow);\n wrapper.appendChild(locContainer);\n if (error) {\n const errorEl = document.createElement('div');\n errorEl.className = 'forms-expert-error-message';\n errorEl.textContent = error;\n wrapper.appendChild(errorEl);\n }\n group.appendChild(wrapper);\n return group;\n }\n\n default:\n input = document.createElement('input');\n input.type = field.type === 'email' ? 'email' : field.type === 'number' ? 'number' : field.type === 'date' ? 'date' : 'text';\n input.className = 'forms-expert-input';\n input.value = String(value || '');\n if (field.type === 'number') {\n if (field.min !== undefined) input.min = String(field.min);\n if (field.max !== undefined) input.max = String(field.max);\n if (field.step !== undefined) input.step = String(field.step);\n }\n break;\n }\n \n input!.id = `mira-field-${field.name}`;\n input!.name = field.name;\n if (field.placeholder && 'placeholder' in input!) {\n (input as HTMLInputElement | HTMLTextAreaElement).placeholder = field.placeholder;\n }\n if (field.required) input!.required = true;\n \n if (error) {\n input!.classList.add('forms-expert-error');\n }\n \n wrapper.appendChild(input!);\n \n // Error message\n if (error) {\n const errorEl = document.createElement('div');\n errorEl.className = 'forms-expert-error-message';\n errorEl.textContent = error;\n wrapper.appendChild(errorEl);\n }\n \n group.appendChild(wrapper);\n \n return group;\n}\n\n/**\n * Render the entire form\n */\nexport function renderForm(\n schema: FormSchema,\n values: Record<string, unknown> = {},\n errors: Record<string, string> = {},\n options: {\n honeypot?: boolean;\n showBranding?: boolean;\n brandingText?: string;\n brandingUrl?: string;\n submitText?: string;\n isLoading?: boolean;\n } = {}\n): HTMLFormElement {\n const form = document.createElement('form');\n form.className = 'forms-expert';\n \n // Render fields\n schema.fields.forEach((field) => {\n const fieldEl = renderField(field, values[field.name], errors[field.name]);\n form.appendChild(fieldEl);\n });\n \n // Honeypot\n if (options.honeypot) {\n const honeypot = document.createElement('input');\n honeypot.type = 'text';\n honeypot.name = '_hp';\n honeypot.className = 'forms-expert-honeypot';\n honeypot.tabIndex = -1;\n honeypot.autocomplete = 'off';\n form.appendChild(honeypot);\n }\n \n // Hidden page URL\n const pageUrl = document.createElement('input');\n pageUrl.type = 'hidden';\n pageUrl.name = 'pageUrl';\n pageUrl.value = typeof window !== 'undefined' ? window.location.href : '';\n form.appendChild(pageUrl);\n \n // Submit button\n const button = document.createElement('button');\n button.type = 'submit';\n button.className = 'forms-expert-button';\n button.disabled = options.isLoading || false;\n \n if (options.isLoading) {\n button.innerHTML = `\n <span class=\"forms-expert-button-loading\">\n <span class=\"forms-expert-spinner\"></span>\n Submitting...\n </span>\n `;\n } else {\n button.textContent = options.submitText || 'Submit';\n }\n \n form.appendChild(button);\n \n // Branding\n if (options.showBranding !== false) {\n const brandingText = options.brandingText || 'Powered by Forms Expert';\n const brandingUrl = options.brandingUrl || 'https://mira.io';\n const branding = document.createElement('div');\n branding.className = 'forms-expert-branding';\n branding.innerHTML = `<a href=\"${brandingUrl}\" target=\"_blank\" rel=\"noopener\">${brandingText}</a>`;\n form.appendChild(branding);\n }\n \n return form;\n}\n\n/**\n * Render success message\n */\nexport function renderSuccess(message: string): HTMLElement {\n const container = document.createElement('div');\n container.className = 'forms-expert-success';\n \n container.innerHTML = `\n <svg class=\"forms-expert-success-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 13l4 4L19 7\"></path>\n </svg>\n <div class=\"forms-expert-success-message\">${escapeHtml(message)}</div>\n `;\n \n return container;\n}\n\n/**\n * Convert validation errors array to record\n */\nexport function errorsToRecord(errors: ValidationError[]): Record<string, string> {\n return errors.reduce(\n (acc, err) => ({ ...acc, [err.field]: err.message }),\n {}\n );\n}\n","/**\n * Form field types\n */\nexport type BasicFieldType =\n | 'text' | 'email' | 'number' | 'textarea' | 'select'\n | 'checkbox' | 'file' | 'date' | 'hidden';\n\nexport type InteractiveFieldType =\n | 'radio' | 'multiselect' | 'rating' | 'scale' | 'toggle'\n | 'ranking' | 'imageChoice' | 'phone' | 'url' | 'password'\n | 'richText' | 'slider' | 'currency' | 'time' | 'datetime'\n | 'dateRange' | 'address' | 'name' | 'dropdown' | 'colorPicker'\n | 'location' | 'opinionScale' | 'consent';\n\nexport type LayoutFieldType = 'heading' | 'divider' | 'paragraph';\n\nexport type FormFieldType = BasicFieldType | InteractiveFieldType | LayoutFieldType;\n\nexport interface FormFieldOption {\n label: string;\n value: string;\n imageUrl?: string;\n}\n\n/**\n * Form field definition\n */\nexport interface FormField {\n name: string;\n type: FormFieldType;\n label?: string;\n placeholder?: string;\n required?: boolean;\n options?: string[] | FormFieldOption[];\n defaultValue?: unknown;\n maxFileSize?: number;\n allowedMimeTypes?: string[];\n multiple?: boolean;\n min?: number;\n max?: number;\n step?: number;\n ratingMax?: number;\n lowLabel?: string;\n highLabel?: string;\n defaultCountryCode?: string;\n currencyCode?: string;\n currencySymbol?: string;\n addressFields?: ('street' | 'street2' | 'city' | 'state' | 'zip' | 'country')[];\n nameFields?: ('prefix' | 'first' | 'middle' | 'last' | 'suffix')[];\n content?: string;\n consentText?: string;\n consentUrl?: string;\n maxLength?: number;\n stepId?: string;\n visibleWhen?: {\n field: string;\n operator: 'eq' | 'neq' | 'contains' | 'gt' | 'lt';\n value: unknown;\n };\n}\n\n/**\n * Form styling configuration\n */\nexport interface FormStyling {\n theme: 'light' | 'dark' | 'system';\n primaryColor: string;\n backgroundColor: string;\n textColor: string;\n borderRadius: 'none' | 'sm' | 'md' | 'lg';\n fontSize: 'sm' | 'md' | 'lg';\n buttonStyle: 'filled' | 'outline';\n labelPosition: 'top' | 'left' | 'floating';\n customCss?: string;\n}\n\n/**\n * Form schema\n */\nexport interface FormSchema {\n fields: FormField[];\n styling?: FormStyling;\n}\n\n/**\n * Form captcha settings\n */\nexport interface CaptchaSettings {\n enabled: boolean;\n provider?: 'turnstile' | 'recaptcha' | 'hcaptcha';\n siteKey?: string;\n}\n\n/**\n * Form branding configuration\n */\nexport interface FormBranding {\n enabled: boolean;\n text?: string;\n url?: string;\n}\n\n/**\n * Form status response from API\n */\nexport interface FormStatusResponse {\n active: boolean;\n formId?: string;\n name?: string;\n mode?: 'free' | 'schema';\n schema?: FormSchema;\n error?: string;\n settings?: {\n captcha: CaptchaSettings;\n honeypot: boolean;\n allowAttachments: boolean;\n maxAttachments?: number;\n maxAttachmentSize?: number;\n successMessage?: string;\n redirectUrl?: string;\n };\n /** Branding configuration */\n branding?: FormBranding;\n /** Available published translation language codes */\n availableLanguages?: string[];\n /** Current language applied to this response (null if base language) */\n currentLanguage?: string | null;\n}\n\n/**\n * Validation error\n */\nexport interface ValidationError {\n field: string;\n message: string;\n}\n\n/**\n * Validation response from API\n */\nexport interface ValidationResponse {\n valid: boolean;\n errors: ValidationError[];\n}\n\n/**\n * Submission response from API\n */\nexport interface SubmissionResponse {\n success: boolean;\n submissionId: string;\n message: string;\n}\n\n/**\n * Submit options\n */\nexport interface SubmitOptions {\n pageUrl?: string;\n captchaToken?: string;\n /** Callback for upload progress */\n onProgress?: (progress: UploadProgress) => void;\n}\n\n/**\n * Upload progress information\n */\nexport interface UploadProgress {\n loaded: number;\n total: number;\n percentage: number;\n}\n\n/**\n * File validation error\n */\nexport interface FileValidationError {\n field: string;\n file: string;\n error: 'size' | 'type' | 'count';\n message: string;\n}\n\n/**\n * SDK configuration\n */\nexport interface FormsSDKConfig {\n apiKey: string;\n resourceId: string;\n baseUrl?: string;\n}\n\n/**\n * Form handler options\n */\nexport interface FormHandlerOptions {\n /** Track form views for analytics (completion rate) */\n trackViews?: boolean;\n onSubmitStart?: () => void;\n onSubmitSuccess?: (response: SubmissionResponse) => void;\n onSubmitError?: (error: FormsError) => void;\n onValidationError?: (errors: ValidationError[]) => void;\n}\n\n/**\n * Forms SDK error\n */\nexport class FormsError extends Error {\n constructor(\n message: string,\n public code: string,\n public statusCode: number,\n public retryAfter?: number\n ) {\n super(message);\n this.name = 'FormsError';\n }\n}\n\n/**\n * Validation error class\n */\nexport class FormValidationError extends Error {\n constructor(public errors: ValidationError[]) {\n super('Validation failed');\n this.name = 'FormValidationError';\n }\n}\n","import {\n FormsSDKConfig,\n FormStatusResponse,\n ValidationResponse,\n SubmissionResponse,\n SubmitOptions,\n FormsError,\n UploadProgress,\n} from './types';\n\n/**\n * API client for forms backend\n */\nexport class FormsApiClient {\n private baseUrl: string;\n private apiKey: string;\n private resourceId: string;\n\n constructor(config: FormsSDKConfig) {\n this.apiKey = config.apiKey;\n this.resourceId = config.resourceId;\n this.baseUrl = (config.baseUrl || 'https://api.forms.expert/api/v1').replace(/\\/$/, '');\n }\n\n /**\n * Build URL with token query parameter\n */\n private buildUrl(path: string): string {\n const separator = path.includes('?') ? '&' : '?';\n return `${this.baseUrl}${path}${separator}token=${encodeURIComponent(this.apiKey)}`;\n }\n\n /**\n * Make an API request\n */\n private async request<T>(\n method: string,\n path: string,\n body?: object\n ): Promise<T> {\n const url = this.buildUrl(path);\n\n const response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n\n const data = await response.json();\n\n if (!response.ok) {\n throw new FormsError(\n data.message || 'Request failed',\n data.code || 'UNKNOWN_ERROR',\n response.status,\n data.retryAfter\n );\n }\n\n return data;\n }\n\n /**\n * Check if form is active and get configuration\n */\n async isActive(slug: string, lang?: string): Promise<FormStatusResponse> {\n const langParam = lang ? `?lang=${encodeURIComponent(lang)}` : '';\n return this.request('GET', `/f/${this.resourceId}/${slug}/is-active${langParam}`);\n }\n\n /**\n * Validate form data without submitting\n */\n async validate(\n slug: string,\n data: Record<string, unknown>\n ): Promise<ValidationResponse> {\n return this.request('POST', `/f/${this.resourceId}/${slug}/validate`, {\n data,\n });\n }\n\n /**\n * Submit form data (supports files)\n */\n async submit(\n slug: string,\n data: Record<string, unknown>,\n options?: SubmitOptions\n ): Promise<SubmissionResponse> {\n const url = this.buildUrl(`/f/${this.resourceId}/${slug}`);\n \n // Check if data contains files\n const hasFiles = Object.values(data).some(\n (v) => v instanceof File || (v instanceof FileList && v.length > 0)\n );\n\n if (hasFiles || options?.onProgress) {\n // Use FormData and XMLHttpRequest for file uploads with progress\n return this.submitWithFormData(url, data, options);\n }\n\n // Use regular JSON request for non-file submissions\n return this.request('POST', `/f/${this.resourceId}/${slug}`, {\n data,\n pageUrl: options?.pageUrl || (typeof window !== 'undefined' ? window.location.href : undefined),\n captchaToken: options?.captchaToken,\n });\n }\n\n /**\n * Submit with FormData (for file uploads with progress tracking)\n */\n private submitWithFormData(\n url: string,\n data: Record<string, unknown>,\n options?: SubmitOptions\n ): Promise<SubmissionResponse> {\n return new Promise((resolve, reject) => {\n const formData = new FormData();\n\n // Add data fields\n for (const [key, value] of Object.entries(data)) {\n if (value instanceof File) {\n formData.append(key, value);\n } else if (value instanceof FileList) {\n Array.from(value).forEach((file) => formData.append(key, file));\n } else if (value !== undefined && value !== null) {\n formData.append(`data[${key}]`, String(value));\n }\n }\n\n // Add metadata\n const pageUrl = options?.pageUrl || (typeof window !== 'undefined' ? window.location.href : '');\n if (pageUrl) {\n formData.append('pageUrl', pageUrl);\n }\n if (options?.captchaToken) {\n formData.append('captchaToken', options.captchaToken);\n }\n\n const xhr = new XMLHttpRequest();\n\n // Progress tracking\n if (options?.onProgress) {\n xhr.upload.addEventListener('progress', (event) => {\n if (event.lengthComputable) {\n options.onProgress!({\n loaded: event.loaded,\n total: event.total,\n percentage: Math.round((event.loaded / event.total) * 100),\n });\n }\n });\n }\n\n xhr.addEventListener('load', () => {\n try {\n const response = JSON.parse(xhr.responseText);\n if (xhr.status >= 200 && xhr.status < 300) {\n resolve(response);\n } else {\n reject(new FormsError(\n response.message || 'Submission failed',\n response.code || 'UNKNOWN_ERROR',\n xhr.status,\n response.retryAfter\n ));\n }\n } catch {\n reject(new FormsError('Invalid response', 'PARSE_ERROR', xhr.status));\n }\n });\n\n xhr.addEventListener('error', () => {\n reject(new FormsError('Network error', 'NETWORK_ERROR', 0));\n });\n\n xhr.addEventListener('abort', () => {\n reject(new FormsError('Request aborted', 'ABORTED', 0));\n });\n\n xhr.open('POST', url);\n xhr.send(formData);\n });\n }\n\n /**\n * Track a form view (for analytics completion rate)\n */\n async trackView(slug: string): Promise<void> {\n const url = this.buildUrl(`/f/${this.resourceId}/${slug}/view`);\n await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' } }).catch(() => {});\n }\n\n /**\n * Get resource ID\n */\n getResourceId(): string {\n return this.resourceId;\n }\n\n /**\n * Get base URL\n */\n getBaseUrl(): string {\n return this.baseUrl;\n }\n}\n","import { FormsApiClient } from './api-client';\nimport {\n FormsSDKConfig,\n FormStatusResponse,\n ValidationResponse,\n SubmissionResponse,\n SubmitOptions,\n FormHandlerOptions,\n FormsError,\n FormValidationError,\n} from './types';\n\n/**\n * Form handler for a specific form\n */\nexport class FormHandler {\n private apiClient: FormsApiClient;\n private slug: string;\n private config: FormStatusResponse | null = null;\n private options: FormHandlerOptions;\n\n constructor(\n apiClient: FormsApiClient,\n slug: string,\n options: FormHandlerOptions = {}\n ) {\n this.apiClient = apiClient;\n this.slug = slug;\n this.options = options;\n }\n\n /**\n * Initialize form handler and fetch configuration\n */\n async initialize(lang?: string): Promise<FormStatusResponse> {\n this.config = await this.apiClient.isActive(this.slug, lang);\n if (this.options.trackViews) {\n this.apiClient.trackView(this.slug);\n }\n return this.config;\n }\n\n /**\n * Get cached form configuration\n */\n getConfig(): FormStatusResponse | null {\n return this.config;\n }\n\n /**\n * Check if form is active\n */\n isActive(): boolean {\n return this.config?.active ?? false;\n }\n\n /**\n * Check if captcha is required\n */\n requiresCaptcha(): boolean {\n return this.config?.settings?.captcha?.enabled ?? false;\n }\n\n /**\n * Get captcha provider\n */\n getCaptchaProvider(): 'turnstile' | 'recaptcha' | 'hcaptcha' | undefined {\n return this.config?.settings?.captcha?.provider;\n }\n\n /**\n * Get form schema\n */\n getSchema() {\n return this.config?.schema;\n }\n\n /**\n * Validate form data\n */\n async validate(data: Record<string, unknown>): Promise<ValidationResponse> {\n return this.apiClient.validate(this.slug, data);\n }\n\n /**\n * Submit form data\n */\n async submit(\n data: Record<string, unknown>,\n options?: SubmitOptions\n ): Promise<SubmissionResponse> {\n this.options.onSubmitStart?.();\n\n try {\n // Validate first if in schema mode\n if (this.config?.mode === 'schema') {\n const validation = await this.validate(data);\n if (!validation.valid) {\n this.options.onValidationError?.(validation.errors);\n throw new FormValidationError(validation.errors);\n }\n }\n\n const response = await this.apiClient.submit(this.slug, data, options);\n this.options.onSubmitSuccess?.(response);\n return response;\n } catch (error) {\n if (error instanceof FormsError) {\n this.options.onSubmitError?.(error);\n }\n throw error;\n }\n }\n\n /**\n * Get success message from config\n */\n getSuccessMessage(): string {\n return this.config?.settings?.successMessage || 'Form submitted successfully!';\n }\n\n /**\n * Get redirect URL from config\n */\n getRedirectUrl(): string | undefined {\n return this.config?.settings?.redirectUrl;\n }\n}\n\n/**\n * Main Forms SDK class\n */\nexport class FormsSDK {\n private apiClient: FormsApiClient;\n\n constructor(config: FormsSDKConfig) {\n this.apiClient = new FormsApiClient(config);\n }\n\n /**\n * Check if form is active and get configuration\n */\n async isActive(slug: string, lang?: string): Promise<FormStatusResponse> {\n return this.apiClient.isActive(slug, lang);\n }\n\n /**\n * Validate form data without submitting\n */\n async validate(\n slug: string,\n data: Record<string, unknown>\n ): Promise<ValidationResponse> {\n return this.apiClient.validate(slug, data);\n }\n\n /**\n * Submit form data\n */\n async submit(\n slug: string,\n data: Record<string, unknown>,\n options?: SubmitOptions\n ): Promise<SubmissionResponse> {\n return this.apiClient.submit(slug, data, options);\n }\n\n /**\n * Create a form handler for a specific form\n */\n form(slug: string, options?: FormHandlerOptions): FormHandler {\n return new FormHandler(this.apiClient, slug, options);\n }\n\n /**\n * Track a form view (for analytics completion rate)\n */\n async trackView(slug: string): Promise<void> {\n return this.apiClient.trackView(slug);\n }\n\n /**\n * Submit with retry logic for rate limits\n */\n async submitWithRetry(\n slug: string,\n data: Record<string, unknown>,\n options?: SubmitOptions & { maxRetries?: number }\n ): Promise<SubmissionResponse> {\n const maxRetries = options?.maxRetries ?? 3;\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n try {\n return await this.submit(slug, data, options);\n } catch (error) {\n lastError = error as Error;\n\n if (error instanceof FormsError) {\n // Don't retry validation or auth errors\n if (\n [\n 'VALIDATION_ERROR',\n 'CAPTCHA_REQUIRED',\n 'ORIGIN_NOT_ALLOWED',\n ].includes(error.code)\n ) {\n throw error;\n }\n\n // Retry rate limits with backoff\n if (error.code.includes('RATE_LIMIT')) {\n const retryAfter = error.retryAfter || Math.pow(2, attempt) * 1000;\n await new Promise((resolve) => setTimeout(resolve, retryAfter));\n continue;\n }\n }\n\n // Exponential backoff for network errors\n await new Promise((resolve) =>\n setTimeout(resolve, Math.pow(2, attempt) * 1000)\n );\n }\n }\n\n throw lastError!;\n }\n}\n","import {\n FormsSDK,\n FormsSDKConfig,\n FormStatusResponse,\n SubmissionResponse,\n ValidationError,\n FormValidationError,\n FormsError,\n} from '../core';\nimport { generateFormStyles } from './styles';\nimport { renderForm, renderSuccess, errorsToRecord } from './renderer';\n\nexport interface FormWidgetOptions {\n /** Target element or selector */\n target: string | HTMLElement;\n /** Form slug */\n slug: string;\n /** Track form views for analytics (completion rate). Default: false */\n trackViews?: boolean;\n /** Custom submit button text */\n submitText?: string;\n /** Success callback */\n onSuccess?: (response: SubmissionResponse) => void;\n /** Error callback */\n onError?: (error: Error) => void;\n /** Validation error callback */\n onValidationError?: (errors: ValidationError[]) => void;\n /** Reset form after submission */\n resetOnSuccess?: boolean;\n /** Redirect after success (overrides form config) */\n redirectUrl?: string;\n /** Language code to pass to backend */\n lang?: string;\n}\n\n/**\n * Form widget for embedding forms\n */\nexport class FormWidget {\n private sdk: FormsSDK;\n private container: HTMLElement;\n private config: FormStatusResponse | null = null;\n private values: Record<string, unknown> = {};\n private errors: Record<string, string> = {};\n private isLoading = false;\n private isSubmitted = false;\n private options: FormWidgetOptions;\n private styleEl: HTMLStyleElement | null = null;\n\n constructor(sdkConfig: FormsSDKConfig, options: FormWidgetOptions) {\n this.sdk = new FormsSDK(sdkConfig);\n this.options = options;\n\n // Get container element\n const target = options.target;\n if (typeof target === 'string') {\n const el = document.querySelector(target);\n if (!el) throw new Error(`Element not found: ${target}`);\n this.container = el as HTMLElement;\n } else {\n this.container = target;\n }\n }\n\n /**\n * Initialize and render the form\n */\n async init(): Promise<void> {\n try {\n this.config = await this.sdk.isActive(this.options.slug, this.options.lang);\n\n if (!this.config.active) {\n this.renderError('This form is not available');\n return;\n }\n\n if (this.options.trackViews) {\n this.sdk.trackView(this.options.slug);\n }\n\n this.injectStyles();\n this.render();\n } catch (error) {\n this.renderError('Failed to load form');\n this.options.onError?.(error as Error);\n }\n }\n\n /**\n * Inject CSS styles\n */\n private injectStyles(): void {\n if (this.styleEl) return;\n\n this.styleEl = document.createElement('style');\n this.styleEl.id = `forms-expert-styles-${this.options.slug}`;\n this.styleEl.textContent = generateFormStyles(this.config?.schema?.styling);\n document.head.appendChild(this.styleEl);\n }\n\n /**\n * Render the form\n */\n private render(): void {\n if (!this.config?.schema) return;\n\n if (this.isSubmitted) {\n this.container.innerHTML = '';\n const successMessage =\n this.config.settings?.successMessage || 'Form submitted successfully!';\n this.container.appendChild(renderSuccess(successMessage));\n return;\n }\n\n const form = renderForm(this.config.schema, this.values, this.errors, {\n honeypot: this.config.settings?.honeypot,\n showBranding: this.config.branding?.enabled !== false,\n brandingText: this.config.branding?.text,\n brandingUrl: this.config.branding?.url,\n submitText: this.options.submitText,\n isLoading: this.isLoading,\n });\n\n // Handle input changes\n form.addEventListener('input', (e) => {\n const target = e.target as HTMLInputElement;\n if (target.name && target.name !== '_hp' && target.name !== 'pageUrl') {\n if (target.type === 'checkbox') {\n this.values[target.name] = target.checked;\n } else if (target.type === 'file') {\n // Store FileList for multiple files, single File for single uploads\n this.values[target.name] = target.multiple ? target.files : target.files?.[0];\n } else {\n this.values[target.name] = target.value;\n }\n // Clear error on change\n if (this.errors[target.name]) {\n delete this.errors[target.name];\n this.render();\n }\n }\n });\n\n // Handle submit\n form.addEventListener('submit', (e) => {\n e.preventDefault();\n this.handleSubmit();\n });\n\n this.container.innerHTML = '';\n this.container.appendChild(form);\n }\n\n /**\n * Handle form submission\n */\n private async handleSubmit(): Promise<void> {\n if (this.isLoading || !this.config) return;\n\n this.isLoading = true;\n this.errors = {};\n this.render();\n\n try {\n const response = await this.sdk.submit(this.options.slug, this.values);\n\n this.isLoading = false;\n this.isSubmitted = true;\n this.render();\n\n this.options.onSuccess?.(response);\n\n // Handle redirect\n const redirectUrl =\n this.options.redirectUrl || this.config.settings?.redirectUrl;\n if (redirectUrl) {\n setTimeout(() => {\n window.location.href = redirectUrl;\n }, 1500);\n }\n\n // Reset form after delay if configured\n if (this.options.resetOnSuccess) {\n setTimeout(() => {\n this.reset();\n }, 3000);\n }\n } catch (error) {\n this.isLoading = false;\n\n if (error instanceof FormValidationError) {\n this.errors = errorsToRecord(error.errors);\n this.options.onValidationError?.(error.errors);\n } else {\n this.options.onError?.(error as Error);\n }\n\n this.render();\n }\n }\n\n /**\n * Reset form to initial state\n */\n reset(): void {\n this.values = {};\n this.errors = {};\n this.isLoading = false;\n this.isSubmitted = false;\n this.render();\n }\n\n /**\n * Render error message\n */\n private renderError(message: string): void {\n this.container.innerHTML = `\n <div class=\"forms-expert\" style=\"text-align: center; padding: 2rem; color: #ef4444;\">\n <p>${message}</p>\n </div>\n `;\n }\n\n /**\n * Destroy widget\n */\n destroy(): void {\n this.container.innerHTML = '';\n this.styleEl?.remove();\n this.styleEl = null;\n }\n}\n\n/**\n * Auto-initialize forms on page load\n */\nexport function autoInit(): void {\n const forms = document.querySelectorAll('[data-forms-expert]');\n\n forms.forEach((el) => {\n const apiKey = el.getAttribute('data-api-key');\n const resourceId = el.getAttribute('data-resource-id');\n const slug = el.getAttribute('data-forms-expert');\n const baseUrl = el.getAttribute('data-base-url') || undefined;\n\n if (!apiKey || !resourceId || !slug) {\n console.error('Forms Expert: Missing required attributes', {\n apiKey: !!apiKey,\n resourceId: !!resourceId,\n slug: !!slug,\n });\n return;\n }\n\n const widget = new FormWidget(\n { apiKey, resourceId, baseUrl },\n {\n target: el as HTMLElement,\n slug,\n trackViews: el.getAttribute('data-track-views') === 'true',\n submitText: el.getAttribute('data-submit-text') || undefined,\n resetOnSuccess: el.getAttribute('data-reset') === 'true',\n lang: el.getAttribute('data-lang') || undefined,\n }\n );\n\n widget.init();\n });\n}\n\n// Auto-init on DOMContentLoaded\nif (typeof window !== 'undefined') {\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', autoInit);\n } else {\n autoInit();\n }\n}\n"],"mappings":"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","names":["defaultStyling","getBorderRadius","radius","getFontSize","size","generateFormStyles","styling","s","fontSize","escapeHtml","text","div","renderField","field","value","error","group","h","p","hr","input","label","text","escapeHtml","link","errorEl","wrapper","defaultOption","opt","option","radioGroup","radioWrapper","radioInput","checkGroup","selected","checkWrapper","checkInput","ratingContainer","max","current","star","scaleContainer","min","i","btn","labels","rangeContainer","range","startInput","endInput","addrContainer","addrFields","addr","af","inp","nameContainer","nameFields","nameVal","nf","imgContainer","opts","card","img","lbl","rankContainer","item","row","locContainer","loc","addrInput","coordRow","latInput","lngInput","renderForm","schema","values","errors","options","form","fieldEl","honeypot","pageUrl","button","brandingText","brandingUrl","branding","renderSuccess","message","container","errorsToRecord","acc","err","FormsError","message","code","statusCode","retryAfter","FormValidationError","errors","FormsApiClient","config","path","separator","method","body","url","response","data","FormsError","slug","lang","langParam","options","v","resolve","reject","formData","key","value","file","pageUrl","xhr","event","FormHandler","apiClient","slug","options","lang","data","validation","FormValidationError","response","error","FormsError","FormsSDK","config","FormsApiClient","maxRetries","lastError","attempt","retryAfter","resolve","FormWidget","sdkConfig","options","FormsSDK","target","el","error","generateFormStyles","successMessage","renderSuccess","form","renderForm","e","response","redirectUrl","FormValidationError","errorsToRecord","message","autoInit","apiKey","resourceId","slug","baseUrl"]}
|
package/dist/vue/index.cjs
CHANGED
|
@@ -52,7 +52,7 @@ var FormsApiClient = class {
|
|
|
52
52
|
constructor(config) {
|
|
53
53
|
this.apiKey = config.apiKey;
|
|
54
54
|
this.resourceId = config.resourceId;
|
|
55
|
-
this.baseUrl = (config.baseUrl || "https://api.
|
|
55
|
+
this.baseUrl = (config.baseUrl || "https://api.forms.expert/api/v1").replace(/\/$/, "");
|
|
56
56
|
}
|
|
57
57
|
/**
|
|
58
58
|
* Build URL with token query parameter
|
|
@@ -464,7 +464,7 @@ function useForm(options) {
|
|
|
464
464
|
}
|
|
465
465
|
|
|
466
466
|
// vue/plugin.ts
|
|
467
|
-
var FORMS_SDK_KEY =
|
|
467
|
+
var FORMS_SDK_KEY = Symbol("forms-sdk");
|
|
468
468
|
function createFormsPlugin(config) {
|
|
469
469
|
const sdk = new FormsSDK(config);
|
|
470
470
|
return {
|
package/dist/vue/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../vue/index.ts","../../vue/use-form.ts","../../core/types.ts","../../core/api-client.ts","../../core/forms-sdk.ts","../../vue/plugin.ts"],"sourcesContent":["// Vue exports\nexport * from './use-form';\nexport * from './plugin';\n","import { ref, computed, onMounted, Ref, ComputedRef } from 'vue';\nimport {\n FormsSDK,\n FormsSDKConfig,\n FormStatusResponse,\n SubmissionResponse,\n ValidationError,\n FormValidationError,\n} from '../core';\n\nexport interface UseFormOptions {\n /** Form slug */\n slug: string;\n /** SDK configuration */\n config: FormsSDKConfig;\n /** Success callback */\n onSuccess?: (response: SubmissionResponse) => void;\n /** Error callback */\n onError?: (error: Error) => void;\n /** Validation error callback */\n onValidationError?: (errors: ValidationError[]) => void;\n /** Auto-initialize on mount */\n autoInit?: boolean;\n /** Language code to pass to backend */\n lang?: string;\n}\n\nexport interface UseFormReturn {\n /** Form configuration */\n config: Ref<FormStatusResponse | null>;\n /** Whether form is loading */\n isLoading: Ref<boolean>;\n /** Whether form is initializing */\n isInitializing: Ref<boolean>;\n /** Whether form was submitted successfully */\n isSubmitted: Ref<boolean>;\n /** Validation errors by field name */\n errors: Ref<Record<string, string>>;\n /** Form values */\n values: Ref<Record<string, unknown>>;\n /** Initialize the form */\n initialize: () => Promise<FormStatusResponse>;\n /** Set a field value */\n setValue: (name: string, value: unknown) => void;\n /** Set multiple values */\n setValues: (values: Record<string, unknown>) => void;\n /** Validate form data */\n validate: () => Promise<boolean>;\n /** Submit the form */\n submit: (captchaToken?: string) => Promise<SubmissionResponse | null>;\n /** Reset form to initial state */\n reset: () => void;\n /** Clear errors */\n clearErrors: () => void;\n /** Whether captcha is required */\n requiresCaptcha: ComputedRef<boolean>;\n /** Captcha provider */\n captchaProvider: ComputedRef<'turnstile' | 'recaptcha' | 'hcaptcha' | undefined>;\n /** Captcha site key */\n captchaSiteKey: ComputedRef<string | undefined>;\n /** Whether honeypot is enabled */\n honeypotEnabled: ComputedRef<boolean>;\n}\n\n/**\n * Vue composable for managing form state and submission\n */\nexport function useForm(options: UseFormOptions): UseFormReturn {\n const sdk = new FormsSDK(options.config);\n\n const config = ref<FormStatusResponse | null>(null);\n const isLoading = ref(false);\n const isInitializing = ref(false);\n const isSubmitted = ref(false);\n const errors = ref<Record<string, string>>({});\n const values = ref<Record<string, unknown>>({});\n\n const initialize = async (): Promise<FormStatusResponse> => {\n isInitializing.value = true;\n try {\n const formConfig = await sdk.isActive(options.slug, options.lang);\n config.value = formConfig;\n return formConfig;\n } finally {\n isInitializing.value = false;\n }\n };\n\n const setValue = (name: string, value: unknown) => {\n values.value = { ...values.value, [name]: value };\n // Clear error on change\n if (errors.value[name]) {\n const { [name]: _, ...rest } = errors.value;\n errors.value = rest;\n }\n };\n\n const setValues = (newValues: Record<string, unknown>) => {\n values.value = { ...values.value, ...newValues };\n };\n\n const validate = async (): Promise<boolean> => {\n const result = await sdk.validate(options.slug, values.value);\n if (!result.valid) {\n const errorMap = result.errors.reduce(\n (acc, err) => ({ ...acc, [err.field]: err.message }),\n {} as Record<string, string>\n );\n errors.value = errorMap;\n }\n return result.valid;\n };\n\n const submit = async (captchaToken?: string): Promise<SubmissionResponse | null> => {\n isLoading.value = true;\n errors.value = {};\n\n try {\n // Add honeypot field if enabled\n const submitData = config.value?.settings?.honeypot\n ? { ...values.value, _hp: '' }\n : values.value;\n\n const response = await sdk.submit(options.slug, submitData, {\n captchaToken,\n });\n isSubmitted.value = true;\n options.onSuccess?.(response);\n return response;\n } catch (error) {\n if (error instanceof FormValidationError) {\n const errorMap = error.errors.reduce(\n (acc, err) => ({ ...acc, [err.field]: err.message }),\n {} as Record<string, string>\n );\n errors.value = errorMap;\n options.onValidationError?.(error.errors);\n } else {\n options.onError?.(error as Error);\n }\n return null;\n } finally {\n isLoading.value = false;\n }\n };\n\n const reset = () => {\n values.value = {};\n errors.value = {};\n isSubmitted.value = false;\n isLoading.value = false;\n };\n\n const clearErrors = () => {\n errors.value = {};\n };\n\n const requiresCaptcha = computed(() => config.value?.settings?.captcha?.enabled ?? false);\n const captchaProvider = computed(() => config.value?.settings?.captcha?.provider);\n const captchaSiteKey = computed(() => config.value?.settings?.captcha?.siteKey);\n const honeypotEnabled = computed(() => config.value?.settings?.honeypot ?? false);\n\n // Auto-initialize\n onMounted(() => {\n if (options.autoInit !== false) {\n initialize();\n }\n });\n\n return {\n config,\n isLoading,\n isInitializing,\n isSubmitted,\n errors,\n values,\n initialize,\n setValue,\n setValues,\n validate,\n submit,\n reset,\n clearErrors,\n requiresCaptcha,\n captchaProvider,\n captchaSiteKey,\n honeypotEnabled,\n };\n}\n","/**\n * Form field types\n */\nexport type BasicFieldType =\n | 'text' | 'email' | 'number' | 'textarea' | 'select'\n | 'checkbox' | 'file' | 'date' | 'hidden';\n\nexport type InteractiveFieldType =\n | 'radio' | 'multiselect' | 'rating' | 'scale' | 'toggle'\n | 'ranking' | 'imageChoice' | 'phone' | 'url' | 'password'\n | 'richText' | 'slider' | 'currency' | 'time' | 'datetime'\n | 'dateRange' | 'address' | 'name' | 'dropdown' | 'colorPicker'\n | 'location' | 'opinionScale' | 'consent';\n\nexport type LayoutFieldType = 'heading' | 'divider' | 'paragraph';\n\nexport type FormFieldType = BasicFieldType | InteractiveFieldType | LayoutFieldType;\n\nexport interface FormFieldOption {\n label: string;\n value: string;\n imageUrl?: string;\n}\n\n/**\n * Form field definition\n */\nexport interface FormField {\n name: string;\n type: FormFieldType;\n label?: string;\n placeholder?: string;\n required?: boolean;\n options?: string[] | FormFieldOption[];\n defaultValue?: unknown;\n maxFileSize?: number;\n allowedMimeTypes?: string[];\n multiple?: boolean;\n min?: number;\n max?: number;\n step?: number;\n ratingMax?: number;\n lowLabel?: string;\n highLabel?: string;\n defaultCountryCode?: string;\n currencyCode?: string;\n currencySymbol?: string;\n addressFields?: ('street' | 'street2' | 'city' | 'state' | 'zip' | 'country')[];\n nameFields?: ('prefix' | 'first' | 'middle' | 'last' | 'suffix')[];\n content?: string;\n consentText?: string;\n consentUrl?: string;\n maxLength?: number;\n stepId?: string;\n visibleWhen?: {\n field: string;\n operator: 'eq' | 'neq' | 'contains' | 'gt' | 'lt';\n value: unknown;\n };\n}\n\n/**\n * Form styling configuration\n */\nexport interface FormStyling {\n theme: 'light' | 'dark' | 'system';\n primaryColor: string;\n backgroundColor: string;\n textColor: string;\n borderRadius: 'none' | 'sm' | 'md' | 'lg';\n fontSize: 'sm' | 'md' | 'lg';\n buttonStyle: 'filled' | 'outline';\n labelPosition: 'top' | 'left' | 'floating';\n customCss?: string;\n}\n\n/**\n * Form schema\n */\nexport interface FormSchema {\n fields: FormField[];\n styling?: FormStyling;\n}\n\n/**\n * Form captcha settings\n */\nexport interface CaptchaSettings {\n enabled: boolean;\n provider?: 'turnstile' | 'recaptcha' | 'hcaptcha';\n siteKey?: string;\n}\n\n/**\n * Form branding configuration\n */\nexport interface FormBranding {\n enabled: boolean;\n text?: string;\n url?: string;\n}\n\n/**\n * Form status response from API\n */\nexport interface FormStatusResponse {\n active: boolean;\n formId?: string;\n name?: string;\n mode?: 'free' | 'schema';\n schema?: FormSchema;\n error?: string;\n settings?: {\n captcha: CaptchaSettings;\n honeypot: boolean;\n allowAttachments: boolean;\n maxAttachments?: number;\n maxAttachmentSize?: number;\n successMessage?: string;\n redirectUrl?: string;\n };\n /** Branding configuration */\n branding?: FormBranding;\n}\n\n/**\n * Validation error\n */\nexport interface ValidationError {\n field: string;\n message: string;\n}\n\n/**\n * Validation response from API\n */\nexport interface ValidationResponse {\n valid: boolean;\n errors: ValidationError[];\n}\n\n/**\n * Submission response from API\n */\nexport interface SubmissionResponse {\n success: boolean;\n submissionId: string;\n message: string;\n}\n\n/**\n * Submit options\n */\nexport interface SubmitOptions {\n pageUrl?: string;\n captchaToken?: string;\n /** Callback for upload progress */\n onProgress?: (progress: UploadProgress) => void;\n}\n\n/**\n * Upload progress information\n */\nexport interface UploadProgress {\n loaded: number;\n total: number;\n percentage: number;\n}\n\n/**\n * File validation error\n */\nexport interface FileValidationError {\n field: string;\n file: string;\n error: 'size' | 'type' | 'count';\n message: string;\n}\n\n/**\n * SDK configuration\n */\nexport interface FormsSDKConfig {\n apiKey: string;\n resourceId: string;\n baseUrl?: string;\n}\n\n/**\n * Form handler options\n */\nexport interface FormHandlerOptions {\n /** Track form views for analytics (completion rate) */\n trackViews?: boolean;\n onSubmitStart?: () => void;\n onSubmitSuccess?: (response: SubmissionResponse) => void;\n onSubmitError?: (error: FormsError) => void;\n onValidationError?: (errors: ValidationError[]) => void;\n}\n\n/**\n * Forms SDK error\n */\nexport class FormsError extends Error {\n constructor(\n message: string,\n public code: string,\n public statusCode: number,\n public retryAfter?: number\n ) {\n super(message);\n this.name = 'FormsError';\n }\n}\n\n/**\n * Validation error class\n */\nexport class FormValidationError extends Error {\n constructor(public errors: ValidationError[]) {\n super('Validation failed');\n this.name = 'FormValidationError';\n }\n}\n","import {\n FormsSDKConfig,\n FormStatusResponse,\n ValidationResponse,\n SubmissionResponse,\n SubmitOptions,\n FormsError,\n UploadProgress,\n} from './types';\n\n/**\n * API client for forms backend\n */\nexport class FormsApiClient {\n private baseUrl: string;\n private apiKey: string;\n private resourceId: string;\n\n constructor(config: FormsSDKConfig) {\n this.apiKey = config.apiKey;\n this.resourceId = config.resourceId;\n this.baseUrl = (config.baseUrl || 'https://api.formsapp.io/api/v1').replace(/\\/$/, '');\n }\n\n /**\n * Build URL with token query parameter\n */\n private buildUrl(path: string): string {\n const separator = path.includes('?') ? '&' : '?';\n return `${this.baseUrl}${path}${separator}token=${encodeURIComponent(this.apiKey)}`;\n }\n\n /**\n * Make an API request\n */\n private async request<T>(\n method: string,\n path: string,\n body?: object\n ): Promise<T> {\n const url = this.buildUrl(path);\n\n const response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n\n const data = await response.json();\n\n if (!response.ok) {\n throw new FormsError(\n data.message || 'Request failed',\n data.code || 'UNKNOWN_ERROR',\n response.status,\n data.retryAfter\n );\n }\n\n return data;\n }\n\n /**\n * Check if form is active and get configuration\n */\n async isActive(slug: string, lang?: string): Promise<FormStatusResponse> {\n const langParam = lang ? `?lang=${encodeURIComponent(lang)}` : '';\n return this.request('GET', `/f/${this.resourceId}/${slug}/is-active${langParam}`);\n }\n\n /**\n * Validate form data without submitting\n */\n async validate(\n slug: string,\n data: Record<string, unknown>\n ): Promise<ValidationResponse> {\n return this.request('POST', `/f/${this.resourceId}/${slug}/validate`, {\n data,\n });\n }\n\n /**\n * Submit form data (supports files)\n */\n async submit(\n slug: string,\n data: Record<string, unknown>,\n options?: SubmitOptions\n ): Promise<SubmissionResponse> {\n const url = this.buildUrl(`/f/${this.resourceId}/${slug}`);\n \n // Check if data contains files\n const hasFiles = Object.values(data).some(\n (v) => v instanceof File || (v instanceof FileList && v.length > 0)\n );\n\n if (hasFiles || options?.onProgress) {\n // Use FormData and XMLHttpRequest for file uploads with progress\n return this.submitWithFormData(url, data, options);\n }\n\n // Use regular JSON request for non-file submissions\n return this.request('POST', `/f/${this.resourceId}/${slug}`, {\n data,\n pageUrl: options?.pageUrl || (typeof window !== 'undefined' ? window.location.href : undefined),\n captchaToken: options?.captchaToken,\n });\n }\n\n /**\n * Submit with FormData (for file uploads with progress tracking)\n */\n private submitWithFormData(\n url: string,\n data: Record<string, unknown>,\n options?: SubmitOptions\n ): Promise<SubmissionResponse> {\n return new Promise((resolve, reject) => {\n const formData = new FormData();\n\n // Add data fields\n for (const [key, value] of Object.entries(data)) {\n if (value instanceof File) {\n formData.append(key, value);\n } else if (value instanceof FileList) {\n Array.from(value).forEach((file) => formData.append(key, file));\n } else if (value !== undefined && value !== null) {\n formData.append(`data[${key}]`, String(value));\n }\n }\n\n // Add metadata\n const pageUrl = options?.pageUrl || (typeof window !== 'undefined' ? window.location.href : '');\n if (pageUrl) {\n formData.append('pageUrl', pageUrl);\n }\n if (options?.captchaToken) {\n formData.append('captchaToken', options.captchaToken);\n }\n\n const xhr = new XMLHttpRequest();\n\n // Progress tracking\n if (options?.onProgress) {\n xhr.upload.addEventListener('progress', (event) => {\n if (event.lengthComputable) {\n options.onProgress!({\n loaded: event.loaded,\n total: event.total,\n percentage: Math.round((event.loaded / event.total) * 100),\n });\n }\n });\n }\n\n xhr.addEventListener('load', () => {\n try {\n const response = JSON.parse(xhr.responseText);\n if (xhr.status >= 200 && xhr.status < 300) {\n resolve(response);\n } else {\n reject(new FormsError(\n response.message || 'Submission failed',\n response.code || 'UNKNOWN_ERROR',\n xhr.status,\n response.retryAfter\n ));\n }\n } catch {\n reject(new FormsError('Invalid response', 'PARSE_ERROR', xhr.status));\n }\n });\n\n xhr.addEventListener('error', () => {\n reject(new FormsError('Network error', 'NETWORK_ERROR', 0));\n });\n\n xhr.addEventListener('abort', () => {\n reject(new FormsError('Request aborted', 'ABORTED', 0));\n });\n\n xhr.open('POST', url);\n xhr.send(formData);\n });\n }\n\n /**\n * Track a form view (for analytics completion rate)\n */\n async trackView(slug: string): Promise<void> {\n const url = this.buildUrl(`/f/${this.resourceId}/${slug}/view`);\n await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' } }).catch(() => {});\n }\n\n /**\n * Get resource ID\n */\n getResourceId(): string {\n return this.resourceId;\n }\n\n /**\n * Get base URL\n */\n getBaseUrl(): string {\n return this.baseUrl;\n }\n}\n","import { FormsApiClient } from './api-client';\nimport {\n FormsSDKConfig,\n FormStatusResponse,\n ValidationResponse,\n SubmissionResponse,\n SubmitOptions,\n FormHandlerOptions,\n FormsError,\n FormValidationError,\n} from './types';\n\n/**\n * Form handler for a specific form\n */\nexport class FormHandler {\n private apiClient: FormsApiClient;\n private slug: string;\n private config: FormStatusResponse | null = null;\n private options: FormHandlerOptions;\n\n constructor(\n apiClient: FormsApiClient,\n slug: string,\n options: FormHandlerOptions = {}\n ) {\n this.apiClient = apiClient;\n this.slug = slug;\n this.options = options;\n }\n\n /**\n * Initialize form handler and fetch configuration\n */\n async initialize(lang?: string): Promise<FormStatusResponse> {\n this.config = await this.apiClient.isActive(this.slug, lang);\n if (this.options.trackViews) {\n this.apiClient.trackView(this.slug);\n }\n return this.config;\n }\n\n /**\n * Get cached form configuration\n */\n getConfig(): FormStatusResponse | null {\n return this.config;\n }\n\n /**\n * Check if form is active\n */\n isActive(): boolean {\n return this.config?.active ?? false;\n }\n\n /**\n * Check if captcha is required\n */\n requiresCaptcha(): boolean {\n return this.config?.settings?.captcha?.enabled ?? false;\n }\n\n /**\n * Get captcha provider\n */\n getCaptchaProvider(): 'turnstile' | 'recaptcha' | 'hcaptcha' | undefined {\n return this.config?.settings?.captcha?.provider;\n }\n\n /**\n * Get form schema\n */\n getSchema() {\n return this.config?.schema;\n }\n\n /**\n * Validate form data\n */\n async validate(data: Record<string, unknown>): Promise<ValidationResponse> {\n return this.apiClient.validate(this.slug, data);\n }\n\n /**\n * Submit form data\n */\n async submit(\n data: Record<string, unknown>,\n options?: SubmitOptions\n ): Promise<SubmissionResponse> {\n this.options.onSubmitStart?.();\n\n try {\n // Validate first if in schema mode\n if (this.config?.mode === 'schema') {\n const validation = await this.validate(data);\n if (!validation.valid) {\n this.options.onValidationError?.(validation.errors);\n throw new FormValidationError(validation.errors);\n }\n }\n\n const response = await this.apiClient.submit(this.slug, data, options);\n this.options.onSubmitSuccess?.(response);\n return response;\n } catch (error) {\n if (error instanceof FormsError) {\n this.options.onSubmitError?.(error);\n }\n throw error;\n }\n }\n\n /**\n * Get success message from config\n */\n getSuccessMessage(): string {\n return this.config?.settings?.successMessage || 'Form submitted successfully!';\n }\n\n /**\n * Get redirect URL from config\n */\n getRedirectUrl(): string | undefined {\n return this.config?.settings?.redirectUrl;\n }\n}\n\n/**\n * Main Forms SDK class\n */\nexport class FormsSDK {\n private apiClient: FormsApiClient;\n\n constructor(config: FormsSDKConfig) {\n this.apiClient = new FormsApiClient(config);\n }\n\n /**\n * Check if form is active and get configuration\n */\n async isActive(slug: string, lang?: string): Promise<FormStatusResponse> {\n return this.apiClient.isActive(slug, lang);\n }\n\n /**\n * Validate form data without submitting\n */\n async validate(\n slug: string,\n data: Record<string, unknown>\n ): Promise<ValidationResponse> {\n return this.apiClient.validate(slug, data);\n }\n\n /**\n * Submit form data\n */\n async submit(\n slug: string,\n data: Record<string, unknown>,\n options?: SubmitOptions\n ): Promise<SubmissionResponse> {\n return this.apiClient.submit(slug, data, options);\n }\n\n /**\n * Create a form handler for a specific form\n */\n form(slug: string, options?: FormHandlerOptions): FormHandler {\n return new FormHandler(this.apiClient, slug, options);\n }\n\n /**\n * Track a form view (for analytics completion rate)\n */\n async trackView(slug: string): Promise<void> {\n return this.apiClient.trackView(slug);\n }\n\n /**\n * Submit with retry logic for rate limits\n */\n async submitWithRetry(\n slug: string,\n data: Record<string, unknown>,\n options?: SubmitOptions & { maxRetries?: number }\n ): Promise<SubmissionResponse> {\n const maxRetries = options?.maxRetries ?? 3;\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n try {\n return await this.submit(slug, data, options);\n } catch (error) {\n lastError = error as Error;\n\n if (error instanceof FormsError) {\n // Don't retry validation or auth errors\n if (\n [\n 'VALIDATION_ERROR',\n 'CAPTCHA_REQUIRED',\n 'ORIGIN_NOT_ALLOWED',\n ].includes(error.code)\n ) {\n throw error;\n }\n\n // Retry rate limits with backoff\n if (error.code.includes('RATE_LIMIT')) {\n const retryAfter = error.retryAfter || Math.pow(2, attempt) * 1000;\n await new Promise((resolve) => setTimeout(resolve, retryAfter));\n continue;\n }\n }\n\n // Exponential backoff for network errors\n await new Promise((resolve) =>\n setTimeout(resolve, Math.pow(2, attempt) * 1000)\n );\n }\n }\n\n throw lastError!;\n }\n}\n","import { App } from 'vue';\nimport { FormsSDKConfig, FormsSDK } from '../core';\n\nconst FORMS_SDK_KEY = Symbol('forms-sdk');\n\n/**\n * Vue plugin for Forms SDK\n */\nexport function createFormsPlugin(config: FormsSDKConfig) {\n const sdk = new FormsSDK(config);\n\n return {\n install(app: App) {\n app.provide(FORMS_SDK_KEY, sdk);\n app.config.globalProperties.$formsSDK = sdk;\n },\n };\n}\n\nexport { FORMS_SDK_KEY };\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../vue/index.ts","../../vue/use-form.ts","../../core/types.ts","../../core/api-client.ts","../../core/forms-sdk.ts","../../vue/plugin.ts"],"sourcesContent":["// Vue exports\nexport * from './use-form';\nexport * from './plugin';\n","import { ref, computed, onMounted, Ref, ComputedRef } from 'vue';\nimport {\n FormsSDK,\n FormsSDKConfig,\n FormStatusResponse,\n SubmissionResponse,\n ValidationError,\n FormValidationError,\n} from '../core';\n\nexport interface UseFormOptions {\n /** Form slug */\n slug: string;\n /** SDK configuration */\n config: FormsSDKConfig;\n /** Success callback */\n onSuccess?: (response: SubmissionResponse) => void;\n /** Error callback */\n onError?: (error: Error) => void;\n /** Validation error callback */\n onValidationError?: (errors: ValidationError[]) => void;\n /** Auto-initialize on mount */\n autoInit?: boolean;\n /** Language code to pass to backend */\n lang?: string;\n}\n\nexport interface UseFormReturn {\n /** Form configuration */\n config: Ref<FormStatusResponse | null>;\n /** Whether form is loading */\n isLoading: Ref<boolean>;\n /** Whether form is initializing */\n isInitializing: Ref<boolean>;\n /** Whether form was submitted successfully */\n isSubmitted: Ref<boolean>;\n /** Validation errors by field name */\n errors: Ref<Record<string, string>>;\n /** Form values */\n values: Ref<Record<string, unknown>>;\n /** Initialize the form */\n initialize: () => Promise<FormStatusResponse>;\n /** Set a field value */\n setValue: (name: string, value: unknown) => void;\n /** Set multiple values */\n setValues: (values: Record<string, unknown>) => void;\n /** Validate form data */\n validate: () => Promise<boolean>;\n /** Submit the form */\n submit: (captchaToken?: string) => Promise<SubmissionResponse | null>;\n /** Reset form to initial state */\n reset: () => void;\n /** Clear errors */\n clearErrors: () => void;\n /** Whether captcha is required */\n requiresCaptcha: ComputedRef<boolean>;\n /** Captcha provider */\n captchaProvider: ComputedRef<'turnstile' | 'recaptcha' | 'hcaptcha' | undefined>;\n /** Captcha site key */\n captchaSiteKey: ComputedRef<string | undefined>;\n /** Whether honeypot is enabled */\n honeypotEnabled: ComputedRef<boolean>;\n}\n\n/**\n * Vue composable for managing form state and submission\n */\nexport function useForm(options: UseFormOptions): UseFormReturn {\n const sdk = new FormsSDK(options.config);\n\n const config = ref<FormStatusResponse | null>(null);\n const isLoading = ref(false);\n const isInitializing = ref(false);\n const isSubmitted = ref(false);\n const errors = ref<Record<string, string>>({});\n const values = ref<Record<string, unknown>>({});\n\n const initialize = async (): Promise<FormStatusResponse> => {\n isInitializing.value = true;\n try {\n const formConfig = await sdk.isActive(options.slug, options.lang);\n config.value = formConfig;\n return formConfig;\n } finally {\n isInitializing.value = false;\n }\n };\n\n const setValue = (name: string, value: unknown) => {\n values.value = { ...values.value, [name]: value };\n // Clear error on change\n if (errors.value[name]) {\n const { [name]: _, ...rest } = errors.value;\n errors.value = rest;\n }\n };\n\n const setValues = (newValues: Record<string, unknown>) => {\n values.value = { ...values.value, ...newValues };\n };\n\n const validate = async (): Promise<boolean> => {\n const result = await sdk.validate(options.slug, values.value);\n if (!result.valid) {\n const errorMap = result.errors.reduce(\n (acc, err) => ({ ...acc, [err.field]: err.message }),\n {} as Record<string, string>\n );\n errors.value = errorMap;\n }\n return result.valid;\n };\n\n const submit = async (captchaToken?: string): Promise<SubmissionResponse | null> => {\n isLoading.value = true;\n errors.value = {};\n\n try {\n // Add honeypot field if enabled\n const submitData = config.value?.settings?.honeypot\n ? { ...values.value, _hp: '' }\n : values.value;\n\n const response = await sdk.submit(options.slug, submitData, {\n captchaToken,\n });\n isSubmitted.value = true;\n options.onSuccess?.(response);\n return response;\n } catch (error) {\n if (error instanceof FormValidationError) {\n const errorMap = error.errors.reduce(\n (acc, err) => ({ ...acc, [err.field]: err.message }),\n {} as Record<string, string>\n );\n errors.value = errorMap;\n options.onValidationError?.(error.errors);\n } else {\n options.onError?.(error as Error);\n }\n return null;\n } finally {\n isLoading.value = false;\n }\n };\n\n const reset = () => {\n values.value = {};\n errors.value = {};\n isSubmitted.value = false;\n isLoading.value = false;\n };\n\n const clearErrors = () => {\n errors.value = {};\n };\n\n const requiresCaptcha = computed(() => config.value?.settings?.captcha?.enabled ?? false);\n const captchaProvider = computed(() => config.value?.settings?.captcha?.provider);\n const captchaSiteKey = computed(() => config.value?.settings?.captcha?.siteKey);\n const honeypotEnabled = computed(() => config.value?.settings?.honeypot ?? false);\n\n // Auto-initialize\n onMounted(() => {\n if (options.autoInit !== false) {\n initialize();\n }\n });\n\n return {\n config,\n isLoading,\n isInitializing,\n isSubmitted,\n errors,\n values,\n initialize,\n setValue,\n setValues,\n validate,\n submit,\n reset,\n clearErrors,\n requiresCaptcha,\n captchaProvider,\n captchaSiteKey,\n honeypotEnabled,\n };\n}\n","/**\n * Form field types\n */\nexport type BasicFieldType =\n | 'text' | 'email' | 'number' | 'textarea' | 'select'\n | 'checkbox' | 'file' | 'date' | 'hidden';\n\nexport type InteractiveFieldType =\n | 'radio' | 'multiselect' | 'rating' | 'scale' | 'toggle'\n | 'ranking' | 'imageChoice' | 'phone' | 'url' | 'password'\n | 'richText' | 'slider' | 'currency' | 'time' | 'datetime'\n | 'dateRange' | 'address' | 'name' | 'dropdown' | 'colorPicker'\n | 'location' | 'opinionScale' | 'consent';\n\nexport type LayoutFieldType = 'heading' | 'divider' | 'paragraph';\n\nexport type FormFieldType = BasicFieldType | InteractiveFieldType | LayoutFieldType;\n\nexport interface FormFieldOption {\n label: string;\n value: string;\n imageUrl?: string;\n}\n\n/**\n * Form field definition\n */\nexport interface FormField {\n name: string;\n type: FormFieldType;\n label?: string;\n placeholder?: string;\n required?: boolean;\n options?: string[] | FormFieldOption[];\n defaultValue?: unknown;\n maxFileSize?: number;\n allowedMimeTypes?: string[];\n multiple?: boolean;\n min?: number;\n max?: number;\n step?: number;\n ratingMax?: number;\n lowLabel?: string;\n highLabel?: string;\n defaultCountryCode?: string;\n currencyCode?: string;\n currencySymbol?: string;\n addressFields?: ('street' | 'street2' | 'city' | 'state' | 'zip' | 'country')[];\n nameFields?: ('prefix' | 'first' | 'middle' | 'last' | 'suffix')[];\n content?: string;\n consentText?: string;\n consentUrl?: string;\n maxLength?: number;\n stepId?: string;\n visibleWhen?: {\n field: string;\n operator: 'eq' | 'neq' | 'contains' | 'gt' | 'lt';\n value: unknown;\n };\n}\n\n/**\n * Form styling configuration\n */\nexport interface FormStyling {\n theme: 'light' | 'dark' | 'system';\n primaryColor: string;\n backgroundColor: string;\n textColor: string;\n borderRadius: 'none' | 'sm' | 'md' | 'lg';\n fontSize: 'sm' | 'md' | 'lg';\n buttonStyle: 'filled' | 'outline';\n labelPosition: 'top' | 'left' | 'floating';\n customCss?: string;\n}\n\n/**\n * Form schema\n */\nexport interface FormSchema {\n fields: FormField[];\n styling?: FormStyling;\n}\n\n/**\n * Form captcha settings\n */\nexport interface CaptchaSettings {\n enabled: boolean;\n provider?: 'turnstile' | 'recaptcha' | 'hcaptcha';\n siteKey?: string;\n}\n\n/**\n * Form branding configuration\n */\nexport interface FormBranding {\n enabled: boolean;\n text?: string;\n url?: string;\n}\n\n/**\n * Form status response from API\n */\nexport interface FormStatusResponse {\n active: boolean;\n formId?: string;\n name?: string;\n mode?: 'free' | 'schema';\n schema?: FormSchema;\n error?: string;\n settings?: {\n captcha: CaptchaSettings;\n honeypot: boolean;\n allowAttachments: boolean;\n maxAttachments?: number;\n maxAttachmentSize?: number;\n successMessage?: string;\n redirectUrl?: string;\n };\n /** Branding configuration */\n branding?: FormBranding;\n /** Available published translation language codes */\n availableLanguages?: string[];\n /** Current language applied to this response (null if base language) */\n currentLanguage?: string | null;\n}\n\n/**\n * Validation error\n */\nexport interface ValidationError {\n field: string;\n message: string;\n}\n\n/**\n * Validation response from API\n */\nexport interface ValidationResponse {\n valid: boolean;\n errors: ValidationError[];\n}\n\n/**\n * Submission response from API\n */\nexport interface SubmissionResponse {\n success: boolean;\n submissionId: string;\n message: string;\n}\n\n/**\n * Submit options\n */\nexport interface SubmitOptions {\n pageUrl?: string;\n captchaToken?: string;\n /** Callback for upload progress */\n onProgress?: (progress: UploadProgress) => void;\n}\n\n/**\n * Upload progress information\n */\nexport interface UploadProgress {\n loaded: number;\n total: number;\n percentage: number;\n}\n\n/**\n * File validation error\n */\nexport interface FileValidationError {\n field: string;\n file: string;\n error: 'size' | 'type' | 'count';\n message: string;\n}\n\n/**\n * SDK configuration\n */\nexport interface FormsSDKConfig {\n apiKey: string;\n resourceId: string;\n baseUrl?: string;\n}\n\n/**\n * Form handler options\n */\nexport interface FormHandlerOptions {\n /** Track form views for analytics (completion rate) */\n trackViews?: boolean;\n onSubmitStart?: () => void;\n onSubmitSuccess?: (response: SubmissionResponse) => void;\n onSubmitError?: (error: FormsError) => void;\n onValidationError?: (errors: ValidationError[]) => void;\n}\n\n/**\n * Forms SDK error\n */\nexport class FormsError extends Error {\n constructor(\n message: string,\n public code: string,\n public statusCode: number,\n public retryAfter?: number\n ) {\n super(message);\n this.name = 'FormsError';\n }\n}\n\n/**\n * Validation error class\n */\nexport class FormValidationError extends Error {\n constructor(public errors: ValidationError[]) {\n super('Validation failed');\n this.name = 'FormValidationError';\n }\n}\n","import {\n FormsSDKConfig,\n FormStatusResponse,\n ValidationResponse,\n SubmissionResponse,\n SubmitOptions,\n FormsError,\n UploadProgress,\n} from './types';\n\n/**\n * API client for forms backend\n */\nexport class FormsApiClient {\n private baseUrl: string;\n private apiKey: string;\n private resourceId: string;\n\n constructor(config: FormsSDKConfig) {\n this.apiKey = config.apiKey;\n this.resourceId = config.resourceId;\n this.baseUrl = (config.baseUrl || 'https://api.forms.expert/api/v1').replace(/\\/$/, '');\n }\n\n /**\n * Build URL with token query parameter\n */\n private buildUrl(path: string): string {\n const separator = path.includes('?') ? '&' : '?';\n return `${this.baseUrl}${path}${separator}token=${encodeURIComponent(this.apiKey)}`;\n }\n\n /**\n * Make an API request\n */\n private async request<T>(\n method: string,\n path: string,\n body?: object\n ): Promise<T> {\n const url = this.buildUrl(path);\n\n const response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n\n const data = await response.json();\n\n if (!response.ok) {\n throw new FormsError(\n data.message || 'Request failed',\n data.code || 'UNKNOWN_ERROR',\n response.status,\n data.retryAfter\n );\n }\n\n return data;\n }\n\n /**\n * Check if form is active and get configuration\n */\n async isActive(slug: string, lang?: string): Promise<FormStatusResponse> {\n const langParam = lang ? `?lang=${encodeURIComponent(lang)}` : '';\n return this.request('GET', `/f/${this.resourceId}/${slug}/is-active${langParam}`);\n }\n\n /**\n * Validate form data without submitting\n */\n async validate(\n slug: string,\n data: Record<string, unknown>\n ): Promise<ValidationResponse> {\n return this.request('POST', `/f/${this.resourceId}/${slug}/validate`, {\n data,\n });\n }\n\n /**\n * Submit form data (supports files)\n */\n async submit(\n slug: string,\n data: Record<string, unknown>,\n options?: SubmitOptions\n ): Promise<SubmissionResponse> {\n const url = this.buildUrl(`/f/${this.resourceId}/${slug}`);\n \n // Check if data contains files\n const hasFiles = Object.values(data).some(\n (v) => v instanceof File || (v instanceof FileList && v.length > 0)\n );\n\n if (hasFiles || options?.onProgress) {\n // Use FormData and XMLHttpRequest for file uploads with progress\n return this.submitWithFormData(url, data, options);\n }\n\n // Use regular JSON request for non-file submissions\n return this.request('POST', `/f/${this.resourceId}/${slug}`, {\n data,\n pageUrl: options?.pageUrl || (typeof window !== 'undefined' ? window.location.href : undefined),\n captchaToken: options?.captchaToken,\n });\n }\n\n /**\n * Submit with FormData (for file uploads with progress tracking)\n */\n private submitWithFormData(\n url: string,\n data: Record<string, unknown>,\n options?: SubmitOptions\n ): Promise<SubmissionResponse> {\n return new Promise((resolve, reject) => {\n const formData = new FormData();\n\n // Add data fields\n for (const [key, value] of Object.entries(data)) {\n if (value instanceof File) {\n formData.append(key, value);\n } else if (value instanceof FileList) {\n Array.from(value).forEach((file) => formData.append(key, file));\n } else if (value !== undefined && value !== null) {\n formData.append(`data[${key}]`, String(value));\n }\n }\n\n // Add metadata\n const pageUrl = options?.pageUrl || (typeof window !== 'undefined' ? window.location.href : '');\n if (pageUrl) {\n formData.append('pageUrl', pageUrl);\n }\n if (options?.captchaToken) {\n formData.append('captchaToken', options.captchaToken);\n }\n\n const xhr = new XMLHttpRequest();\n\n // Progress tracking\n if (options?.onProgress) {\n xhr.upload.addEventListener('progress', (event) => {\n if (event.lengthComputable) {\n options.onProgress!({\n loaded: event.loaded,\n total: event.total,\n percentage: Math.round((event.loaded / event.total) * 100),\n });\n }\n });\n }\n\n xhr.addEventListener('load', () => {\n try {\n const response = JSON.parse(xhr.responseText);\n if (xhr.status >= 200 && xhr.status < 300) {\n resolve(response);\n } else {\n reject(new FormsError(\n response.message || 'Submission failed',\n response.code || 'UNKNOWN_ERROR',\n xhr.status,\n response.retryAfter\n ));\n }\n } catch {\n reject(new FormsError('Invalid response', 'PARSE_ERROR', xhr.status));\n }\n });\n\n xhr.addEventListener('error', () => {\n reject(new FormsError('Network error', 'NETWORK_ERROR', 0));\n });\n\n xhr.addEventListener('abort', () => {\n reject(new FormsError('Request aborted', 'ABORTED', 0));\n });\n\n xhr.open('POST', url);\n xhr.send(formData);\n });\n }\n\n /**\n * Track a form view (for analytics completion rate)\n */\n async trackView(slug: string): Promise<void> {\n const url = this.buildUrl(`/f/${this.resourceId}/${slug}/view`);\n await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' } }).catch(() => {});\n }\n\n /**\n * Get resource ID\n */\n getResourceId(): string {\n return this.resourceId;\n }\n\n /**\n * Get base URL\n */\n getBaseUrl(): string {\n return this.baseUrl;\n }\n}\n","import { FormsApiClient } from './api-client';\nimport {\n FormsSDKConfig,\n FormStatusResponse,\n ValidationResponse,\n SubmissionResponse,\n SubmitOptions,\n FormHandlerOptions,\n FormsError,\n FormValidationError,\n} from './types';\n\n/**\n * Form handler for a specific form\n */\nexport class FormHandler {\n private apiClient: FormsApiClient;\n private slug: string;\n private config: FormStatusResponse | null = null;\n private options: FormHandlerOptions;\n\n constructor(\n apiClient: FormsApiClient,\n slug: string,\n options: FormHandlerOptions = {}\n ) {\n this.apiClient = apiClient;\n this.slug = slug;\n this.options = options;\n }\n\n /**\n * Initialize form handler and fetch configuration\n */\n async initialize(lang?: string): Promise<FormStatusResponse> {\n this.config = await this.apiClient.isActive(this.slug, lang);\n if (this.options.trackViews) {\n this.apiClient.trackView(this.slug);\n }\n return this.config;\n }\n\n /**\n * Get cached form configuration\n */\n getConfig(): FormStatusResponse | null {\n return this.config;\n }\n\n /**\n * Check if form is active\n */\n isActive(): boolean {\n return this.config?.active ?? false;\n }\n\n /**\n * Check if captcha is required\n */\n requiresCaptcha(): boolean {\n return this.config?.settings?.captcha?.enabled ?? false;\n }\n\n /**\n * Get captcha provider\n */\n getCaptchaProvider(): 'turnstile' | 'recaptcha' | 'hcaptcha' | undefined {\n return this.config?.settings?.captcha?.provider;\n }\n\n /**\n * Get form schema\n */\n getSchema() {\n return this.config?.schema;\n }\n\n /**\n * Validate form data\n */\n async validate(data: Record<string, unknown>): Promise<ValidationResponse> {\n return this.apiClient.validate(this.slug, data);\n }\n\n /**\n * Submit form data\n */\n async submit(\n data: Record<string, unknown>,\n options?: SubmitOptions\n ): Promise<SubmissionResponse> {\n this.options.onSubmitStart?.();\n\n try {\n // Validate first if in schema mode\n if (this.config?.mode === 'schema') {\n const validation = await this.validate(data);\n if (!validation.valid) {\n this.options.onValidationError?.(validation.errors);\n throw new FormValidationError(validation.errors);\n }\n }\n\n const response = await this.apiClient.submit(this.slug, data, options);\n this.options.onSubmitSuccess?.(response);\n return response;\n } catch (error) {\n if (error instanceof FormsError) {\n this.options.onSubmitError?.(error);\n }\n throw error;\n }\n }\n\n /**\n * Get success message from config\n */\n getSuccessMessage(): string {\n return this.config?.settings?.successMessage || 'Form submitted successfully!';\n }\n\n /**\n * Get redirect URL from config\n */\n getRedirectUrl(): string | undefined {\n return this.config?.settings?.redirectUrl;\n }\n}\n\n/**\n * Main Forms SDK class\n */\nexport class FormsSDK {\n private apiClient: FormsApiClient;\n\n constructor(config: FormsSDKConfig) {\n this.apiClient = new FormsApiClient(config);\n }\n\n /**\n * Check if form is active and get configuration\n */\n async isActive(slug: string, lang?: string): Promise<FormStatusResponse> {\n return this.apiClient.isActive(slug, lang);\n }\n\n /**\n * Validate form data without submitting\n */\n async validate(\n slug: string,\n data: Record<string, unknown>\n ): Promise<ValidationResponse> {\n return this.apiClient.validate(slug, data);\n }\n\n /**\n * Submit form data\n */\n async submit(\n slug: string,\n data: Record<string, unknown>,\n options?: SubmitOptions\n ): Promise<SubmissionResponse> {\n return this.apiClient.submit(slug, data, options);\n }\n\n /**\n * Create a form handler for a specific form\n */\n form(slug: string, options?: FormHandlerOptions): FormHandler {\n return new FormHandler(this.apiClient, slug, options);\n }\n\n /**\n * Track a form view (for analytics completion rate)\n */\n async trackView(slug: string): Promise<void> {\n return this.apiClient.trackView(slug);\n }\n\n /**\n * Submit with retry logic for rate limits\n */\n async submitWithRetry(\n slug: string,\n data: Record<string, unknown>,\n options?: SubmitOptions & { maxRetries?: number }\n ): Promise<SubmissionResponse> {\n const maxRetries = options?.maxRetries ?? 3;\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n try {\n return await this.submit(slug, data, options);\n } catch (error) {\n lastError = error as Error;\n\n if (error instanceof FormsError) {\n // Don't retry validation or auth errors\n if (\n [\n 'VALIDATION_ERROR',\n 'CAPTCHA_REQUIRED',\n 'ORIGIN_NOT_ALLOWED',\n ].includes(error.code)\n ) {\n throw error;\n }\n\n // Retry rate limits with backoff\n if (error.code.includes('RATE_LIMIT')) {\n const retryAfter = error.retryAfter || Math.pow(2, attempt) * 1000;\n await new Promise((resolve) => setTimeout(resolve, retryAfter));\n continue;\n }\n }\n\n // Exponential backoff for network errors\n await new Promise((resolve) =>\n setTimeout(resolve, Math.pow(2, attempt) * 1000)\n );\n }\n }\n\n throw lastError!;\n }\n}\n","import { App } from 'vue';\nimport { FormsSDKConfig, FormsSDK } from '../core';\n\nconst FORMS_SDK_KEY = Symbol('forms-sdk');\n\n/**\n * Vue plugin for Forms SDK\n */\nexport function createFormsPlugin(config: FormsSDKConfig) {\n const sdk = new FormsSDK(config);\n\n return {\n install(app: App) {\n app.provide(FORMS_SDK_KEY, sdk);\n app.config.globalProperties.$formsSDK = sdk;\n },\n };\n}\n\nexport { FORMS_SDK_KEY };\n"],"mappings":"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","names":[]}
|
package/dist/vue/index.d.cts
CHANGED
|
@@ -106,6 +106,10 @@ interface FormStatusResponse {
|
|
|
106
106
|
};
|
|
107
107
|
/** Branding configuration */
|
|
108
108
|
branding?: FormBranding;
|
|
109
|
+
/** Available published translation language codes */
|
|
110
|
+
availableLanguages?: string[];
|
|
111
|
+
/** Current language applied to this response (null if base language) */
|
|
112
|
+
currentLanguage?: string | null;
|
|
109
113
|
}
|
|
110
114
|
/**
|
|
111
115
|
* Validation error
|