@forms.expert/sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +331 -0
- package/dist/core/index.cjs +365 -0
- package/dist/core/index.cjs.map +1 -0
- package/dist/core/index.d.cts +322 -0
- package/dist/core/index.d.ts +322 -0
- package/dist/core/index.js +334 -0
- package/dist/core/index.js.map +1 -0
- package/dist/react/index.cjs +1014 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +430 -0
- package/dist/react/index.d.ts +430 -0
- package/dist/react/index.js +991 -0
- package/dist/react/index.js.map +1 -0
- package/dist/vanilla/index.cjs +209 -0
- package/dist/vanilla/index.cjs.map +1 -0
- package/dist/vanilla/index.d.cts +188 -0
- package/dist/vanilla/index.d.ts +188 -0
- package/dist/vanilla/index.global.js +209 -0
- package/dist/vanilla/index.global.js.map +1 -0
- package/dist/vanilla/index.js +209 -0
- package/dist/vanilla/index.js.map +1 -0
- package/dist/vue/index.cjs +482 -0
- package/dist/vue/index.cjs.map +1 -0
- package/dist/vue/index.d.cts +197 -0
- package/dist/vue/index.d.ts +197 -0
- package/dist/vue/index.js +453 -0
- package/dist/vue/index.js.map +1 -0
- package/package.json +70 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../core/index.ts","../../core/types.ts","../../core/api-client.ts","../../core/forms-sdk.ts"],"sourcesContent":["// Core exports\nexport * from './types';\nexport * from './api-client';\nexport * from './forms-sdk';\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): Promise<FormStatusResponse> {\n return this.request('GET', `/f/${this.resourceId}/${slug}/is-active`);\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(): Promise<FormStatusResponse> {\n this.config = await this.apiClient.isActive(this.slug);\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): Promise<FormStatusResponse> {\n return this.apiClient.isActive(slug);\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"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;AC2MO,IAAM,aAAN,cAAyB,MAAM;AAAA,EACpC,YACE,SACO,MACA,YACA,YACP;AACA,UAAM,OAAO;AAJN;AACA;AACA;AAGP,SAAK,OAAO;AAAA,EACd;AACF;AAKO,IAAM,sBAAN,cAAkC,MAAM;AAAA,EAC7C,YAAmB,QAA2B;AAC5C,UAAM,mBAAmB;AADR;AAEjB,SAAK,OAAO;AAAA,EACd;AACF;;;AClNO,IAAM,iBAAN,MAAqB;AAAA,EAK1B,YAAY,QAAwB;AAClC,SAAK,SAAS,OAAO;AACrB,SAAK,aAAa,OAAO;AACzB,SAAK,WAAW,OAAO,WAAW,kCAAkC,QAAQ,OAAO,EAAE;AAAA,EACvF;AAAA;AAAA;AAAA;AAAA,EAKQ,SAAS,MAAsB;AACrC,UAAM,YAAY,KAAK,SAAS,GAAG,IAAI,MAAM;AAC7C,WAAO,GAAG,KAAK,OAAO,GAAG,IAAI,GAAG,SAAS,SAAS,mBAAmB,KAAK,MAAM,CAAC;AAAA,EACnF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,QACZ,QACA,MACA,MACY;AACZ,UAAM,MAAM,KAAK,SAAS,IAAI;AAE9B,UAAM,WAAW,MAAM,MAAM,KAAK;AAAA,MAChC;AAAA,MACA,SAAS;AAAA,QACP,gBAAgB;AAAA,MAClB;AAAA,MACA,MAAM,OAAO,KAAK,UAAU,IAAI,IAAI;AAAA,IACtC,CAAC;AAED,UAAM,OAAO,MAAM,SAAS,KAAK;AAEjC,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI;AAAA,QACR,KAAK,WAAW;AAAA,QAChB,KAAK,QAAQ;AAAA,QACb,SAAS;AAAA,QACT,KAAK;AAAA,MACP;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,SAAS,MAA2C;AACxD,WAAO,KAAK,QAAQ,OAAO,MAAM,KAAK,UAAU,IAAI,IAAI,YAAY;AAAA,EACtE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,SACJ,MACA,MAC6B;AAC7B,WAAO,KAAK,QAAQ,QAAQ,MAAM,KAAK,UAAU,IAAI,IAAI,aAAa;AAAA,MACpE;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OACJ,MACA,MACA,SAC6B;AAC7B,UAAM,MAAM,KAAK,SAAS,MAAM,KAAK,UAAU,IAAI,IAAI,EAAE;AAGzD,UAAM,WAAW,OAAO,OAAO,IAAI,EAAE;AAAA,MACnC,CAAC,MAAM,aAAa,QAAS,aAAa,YAAY,EAAE,SAAS;AAAA,IACnE;AAEA,QAAI,YAAY,SAAS,YAAY;AAEnC,aAAO,KAAK,mBAAmB,KAAK,MAAM,OAAO;AAAA,IACnD;AAGA,WAAO,KAAK,QAAQ,QAAQ,MAAM,KAAK,UAAU,IAAI,IAAI,IAAI;AAAA,MAC3D;AAAA,MACA,SAAS,SAAS,YAAY,OAAO,WAAW,cAAc,OAAO,SAAS,OAAO;AAAA,MACrF,cAAc,SAAS;AAAA,IACzB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKQ,mBACN,KACA,MACA,SAC6B;AAC7B,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,WAAW,IAAI,SAAS;AAG9B,iBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAAI,GAAG;AAC/C,YAAI,iBAAiB,MAAM;AACzB,mBAAS,OAAO,KAAK,KAAK;AAAA,QAC5B,WAAW,iBAAiB,UAAU;AACpC,gBAAM,KAAK,KAAK,EAAE,QAAQ,CAAC,SAAS,SAAS,OAAO,KAAK,IAAI,CAAC;AAAA,QAChE,WAAW,UAAU,UAAa,UAAU,MAAM;AAChD,mBAAS,OAAO,QAAQ,GAAG,KAAK,OAAO,KAAK,CAAC;AAAA,QAC/C;AAAA,MACF;AAGA,YAAM,UAAU,SAAS,YAAY,OAAO,WAAW,cAAc,OAAO,SAAS,OAAO;AAC5F,UAAI,SAAS;AACX,iBAAS,OAAO,WAAW,OAAO;AAAA,MACpC;AACA,UAAI,SAAS,cAAc;AACzB,iBAAS,OAAO,gBAAgB,QAAQ,YAAY;AAAA,MACtD;AAEA,YAAM,MAAM,IAAI,eAAe;AAG/B,UAAI,SAAS,YAAY;AACvB,YAAI,OAAO,iBAAiB,YAAY,CAAC,UAAU;AACjD,cAAI,MAAM,kBAAkB;AAC1B,oBAAQ,WAAY;AAAA,cAClB,QAAQ,MAAM;AAAA,cACd,OAAO,MAAM;AAAA,cACb,YAAY,KAAK,MAAO,MAAM,SAAS,MAAM,QAAS,GAAG;AAAA,YAC3D,CAAC;AAAA,UACH;AAAA,QACF,CAAC;AAAA,MACH;AAEA,UAAI,iBAAiB,QAAQ,MAAM;AACjC,YAAI;AACF,gBAAM,WAAW,KAAK,MAAM,IAAI,YAAY;AAC5C,cAAI,IAAI,UAAU,OAAO,IAAI,SAAS,KAAK;AACzC,oBAAQ,QAAQ;AAAA,UAClB,OAAO;AACL,mBAAO,IAAI;AAAA,cACT,SAAS,WAAW;AAAA,cACpB,SAAS,QAAQ;AAAA,cACjB,IAAI;AAAA,cACJ,SAAS;AAAA,YACX,CAAC;AAAA,UACH;AAAA,QACF,QAAQ;AACN,iBAAO,IAAI,WAAW,oBAAoB,eAAe,IAAI,MAAM,CAAC;AAAA,QACtE;AAAA,MACF,CAAC;AAED,UAAI,iBAAiB,SAAS,MAAM;AAClC,eAAO,IAAI,WAAW,iBAAiB,iBAAiB,CAAC,CAAC;AAAA,MAC5D,CAAC;AAED,UAAI,iBAAiB,SAAS,MAAM;AAClC,eAAO,IAAI,WAAW,mBAAmB,WAAW,CAAC,CAAC;AAAA,MACxD,CAAC;AAED,UAAI,KAAK,QAAQ,GAAG;AACpB,UAAI,KAAK,QAAQ;AAAA,IACnB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAU,MAA6B;AAC3C,UAAM,MAAM,KAAK,SAAS,MAAM,KAAK,UAAU,IAAI,IAAI,OAAO;AAC9D,UAAM,MAAM,KAAK,EAAE,QAAQ,QAAQ,SAAS,EAAE,gBAAgB,mBAAmB,EAAE,CAAC,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EACtG;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAwB;AACtB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,aAAqB;AACnB,WAAO,KAAK;AAAA,EACd;AACF;;;AClMO,IAAM,cAAN,MAAkB;AAAA,EAMvB,YACE,WACA,MACA,UAA8B,CAAC,GAC/B;AAPF,SAAQ,SAAoC;AAQ1C,SAAK,YAAY;AACjB,SAAK,OAAO;AACZ,SAAK,UAAU;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAA0C;AAC9C,SAAK,SAAS,MAAM,KAAK,UAAU,SAAS,KAAK,IAAI;AACrD,QAAI,KAAK,QAAQ,YAAY;AAC3B,WAAK,UAAU,UAAU,KAAK,IAAI;AAAA,IACpC;AACA,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,YAAuC;AACrC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,WAAoB;AAClB,WAAO,KAAK,QAAQ,UAAU;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA,EAKA,kBAA2B;AACzB,WAAO,KAAK,QAAQ,UAAU,SAAS,WAAW;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA,EAKA,qBAAyE;AACvE,WAAO,KAAK,QAAQ,UAAU,SAAS;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA,EAKA,YAAY;AACV,WAAO,KAAK,QAAQ;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,SAAS,MAA4D;AACzE,WAAO,KAAK,UAAU,SAAS,KAAK,MAAM,IAAI;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OACJ,MACA,SAC6B;AAC7B,SAAK,QAAQ,gBAAgB;AAE7B,QAAI;AAEF,UAAI,KAAK,QAAQ,SAAS,UAAU;AAClC,cAAM,aAAa,MAAM,KAAK,SAAS,IAAI;AAC3C,YAAI,CAAC,WAAW,OAAO;AACrB,eAAK,QAAQ,oBAAoB,WAAW,MAAM;AAClD,gBAAM,IAAI,oBAAoB,WAAW,MAAM;AAAA,QACjD;AAAA,MACF;AAEA,YAAM,WAAW,MAAM,KAAK,UAAU,OAAO,KAAK,MAAM,MAAM,OAAO;AACrE,WAAK,QAAQ,kBAAkB,QAAQ;AACvC,aAAO;AAAA,IACT,SAAS,OAAO;AACd,UAAI,iBAAiB,YAAY;AAC/B,aAAK,QAAQ,gBAAgB,KAAK;AAAA,MACpC;AACA,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,oBAA4B;AAC1B,WAAO,KAAK,QAAQ,UAAU,kBAAkB;AAAA,EAClD;AAAA;AAAA;AAAA;AAAA,EAKA,iBAAqC;AACnC,WAAO,KAAK,QAAQ,UAAU;AAAA,EAChC;AACF;AAKO,IAAM,WAAN,MAAe;AAAA,EAGpB,YAAY,QAAwB;AAClC,SAAK,YAAY,IAAI,eAAe,MAAM;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,SAAS,MAA2C;AACxD,WAAO,KAAK,UAAU,SAAS,IAAI;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,SACJ,MACA,MAC6B;AAC7B,WAAO,KAAK,UAAU,SAAS,MAAM,IAAI;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OACJ,MACA,MACA,SAC6B;AAC7B,WAAO,KAAK,UAAU,OAAO,MAAM,MAAM,OAAO;AAAA,EAClD;AAAA;AAAA;AAAA;AAAA,EAKA,KAAK,MAAc,SAA2C;AAC5D,WAAO,IAAI,YAAY,KAAK,WAAW,MAAM,OAAO;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAU,MAA6B;AAC3C,WAAO,KAAK,UAAU,UAAU,IAAI;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,gBACJ,MACA,MACA,SAC6B;AAC7B,UAAM,aAAa,SAAS,cAAc;AAC1C,QAAI,YAA0B;AAE9B,aAAS,UAAU,GAAG,UAAU,YAAY,WAAW;AACrD,UAAI;AACF,eAAO,MAAM,KAAK,OAAO,MAAM,MAAM,OAAO;AAAA,MAC9C,SAAS,OAAO;AACd,oBAAY;AAEZ,YAAI,iBAAiB,YAAY;AAE/B,cACE;AAAA,YACE;AAAA,YACA;AAAA,YACA;AAAA,UACF,EAAE,SAAS,MAAM,IAAI,GACrB;AACA,kBAAM;AAAA,UACR;AAGA,cAAI,MAAM,KAAK,SAAS,YAAY,GAAG;AACrC,kBAAM,aAAa,MAAM,cAAc,KAAK,IAAI,GAAG,OAAO,IAAI;AAC9D,kBAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,UAAU,CAAC;AAC9D;AAAA,UACF;AAAA,QACF;AAGA,cAAM,IAAI;AAAA,UAAQ,CAAC,YACjB,WAAW,SAAS,KAAK,IAAI,GAAG,OAAO,IAAI,GAAI;AAAA,QACjD;AAAA,MACF;AAAA,IACF;AAEA,UAAM;AAAA,EACR;AACF;","names":[]}
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Form field types
|
|
3
|
+
*/
|
|
4
|
+
type BasicFieldType = 'text' | 'email' | 'number' | 'textarea' | 'select' | 'checkbox' | 'file' | 'date' | 'hidden';
|
|
5
|
+
type InteractiveFieldType = 'radio' | 'multiselect' | 'rating' | 'scale' | 'toggle' | 'ranking' | 'imageChoice' | 'phone' | 'url' | 'password' | 'richText' | 'slider' | 'currency' | 'time' | 'datetime' | 'dateRange' | 'address' | 'name' | 'dropdown' | 'colorPicker' | 'location' | 'opinionScale' | 'consent';
|
|
6
|
+
type LayoutFieldType = 'heading' | 'divider' | 'paragraph';
|
|
7
|
+
type FormFieldType = BasicFieldType | InteractiveFieldType | LayoutFieldType;
|
|
8
|
+
interface FormFieldOption {
|
|
9
|
+
label: string;
|
|
10
|
+
value: string;
|
|
11
|
+
imageUrl?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Form field definition
|
|
15
|
+
*/
|
|
16
|
+
interface FormField {
|
|
17
|
+
name: string;
|
|
18
|
+
type: FormFieldType;
|
|
19
|
+
label?: string;
|
|
20
|
+
placeholder?: string;
|
|
21
|
+
required?: boolean;
|
|
22
|
+
options?: string[] | FormFieldOption[];
|
|
23
|
+
defaultValue?: unknown;
|
|
24
|
+
maxFileSize?: number;
|
|
25
|
+
allowedMimeTypes?: string[];
|
|
26
|
+
multiple?: boolean;
|
|
27
|
+
min?: number;
|
|
28
|
+
max?: number;
|
|
29
|
+
step?: number;
|
|
30
|
+
ratingMax?: number;
|
|
31
|
+
lowLabel?: string;
|
|
32
|
+
highLabel?: string;
|
|
33
|
+
defaultCountryCode?: string;
|
|
34
|
+
currencyCode?: string;
|
|
35
|
+
currencySymbol?: string;
|
|
36
|
+
addressFields?: ('street' | 'street2' | 'city' | 'state' | 'zip' | 'country')[];
|
|
37
|
+
nameFields?: ('prefix' | 'first' | 'middle' | 'last' | 'suffix')[];
|
|
38
|
+
content?: string;
|
|
39
|
+
consentText?: string;
|
|
40
|
+
consentUrl?: string;
|
|
41
|
+
maxLength?: number;
|
|
42
|
+
stepId?: string;
|
|
43
|
+
visibleWhen?: {
|
|
44
|
+
field: string;
|
|
45
|
+
operator: 'eq' | 'neq' | 'contains' | 'gt' | 'lt';
|
|
46
|
+
value: unknown;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Form styling configuration
|
|
51
|
+
*/
|
|
52
|
+
interface FormStyling {
|
|
53
|
+
theme: 'light' | 'dark' | 'system';
|
|
54
|
+
primaryColor: string;
|
|
55
|
+
backgroundColor: string;
|
|
56
|
+
textColor: string;
|
|
57
|
+
borderRadius: 'none' | 'sm' | 'md' | 'lg';
|
|
58
|
+
fontSize: 'sm' | 'md' | 'lg';
|
|
59
|
+
buttonStyle: 'filled' | 'outline';
|
|
60
|
+
labelPosition: 'top' | 'left' | 'floating';
|
|
61
|
+
customCss?: string;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Form schema
|
|
65
|
+
*/
|
|
66
|
+
interface FormSchema {
|
|
67
|
+
fields: FormField[];
|
|
68
|
+
styling?: FormStyling;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Form captcha settings
|
|
72
|
+
*/
|
|
73
|
+
interface CaptchaSettings {
|
|
74
|
+
enabled: boolean;
|
|
75
|
+
provider?: 'turnstile' | 'recaptcha' | 'hcaptcha';
|
|
76
|
+
siteKey?: string;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Form branding configuration
|
|
80
|
+
*/
|
|
81
|
+
interface FormBranding {
|
|
82
|
+
enabled: boolean;
|
|
83
|
+
text?: string;
|
|
84
|
+
url?: string;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Form status response from API
|
|
88
|
+
*/
|
|
89
|
+
interface FormStatusResponse {
|
|
90
|
+
active: boolean;
|
|
91
|
+
formId?: string;
|
|
92
|
+
name?: string;
|
|
93
|
+
mode?: 'free' | 'schema';
|
|
94
|
+
schema?: FormSchema;
|
|
95
|
+
error?: string;
|
|
96
|
+
settings?: {
|
|
97
|
+
captcha: CaptchaSettings;
|
|
98
|
+
honeypot: boolean;
|
|
99
|
+
allowAttachments: boolean;
|
|
100
|
+
maxAttachments?: number;
|
|
101
|
+
maxAttachmentSize?: number;
|
|
102
|
+
successMessage?: string;
|
|
103
|
+
redirectUrl?: string;
|
|
104
|
+
};
|
|
105
|
+
/** Branding configuration */
|
|
106
|
+
branding?: FormBranding;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Validation error
|
|
110
|
+
*/
|
|
111
|
+
interface ValidationError {
|
|
112
|
+
field: string;
|
|
113
|
+
message: string;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Validation response from API
|
|
117
|
+
*/
|
|
118
|
+
interface ValidationResponse {
|
|
119
|
+
valid: boolean;
|
|
120
|
+
errors: ValidationError[];
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Submission response from API
|
|
124
|
+
*/
|
|
125
|
+
interface SubmissionResponse {
|
|
126
|
+
success: boolean;
|
|
127
|
+
submissionId: string;
|
|
128
|
+
message: string;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Submit options
|
|
132
|
+
*/
|
|
133
|
+
interface SubmitOptions {
|
|
134
|
+
pageUrl?: string;
|
|
135
|
+
captchaToken?: string;
|
|
136
|
+
/** Callback for upload progress */
|
|
137
|
+
onProgress?: (progress: UploadProgress) => void;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Upload progress information
|
|
141
|
+
*/
|
|
142
|
+
interface UploadProgress {
|
|
143
|
+
loaded: number;
|
|
144
|
+
total: number;
|
|
145
|
+
percentage: number;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* File validation error
|
|
149
|
+
*/
|
|
150
|
+
interface FileValidationError {
|
|
151
|
+
field: string;
|
|
152
|
+
file: string;
|
|
153
|
+
error: 'size' | 'type' | 'count';
|
|
154
|
+
message: string;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* SDK configuration
|
|
158
|
+
*/
|
|
159
|
+
interface FormsSDKConfig {
|
|
160
|
+
apiKey: string;
|
|
161
|
+
resourceId: string;
|
|
162
|
+
baseUrl?: string;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Form handler options
|
|
166
|
+
*/
|
|
167
|
+
interface FormHandlerOptions {
|
|
168
|
+
/** Track form views for analytics (completion rate) */
|
|
169
|
+
trackViews?: boolean;
|
|
170
|
+
onSubmitStart?: () => void;
|
|
171
|
+
onSubmitSuccess?: (response: SubmissionResponse) => void;
|
|
172
|
+
onSubmitError?: (error: FormsError) => void;
|
|
173
|
+
onValidationError?: (errors: ValidationError[]) => void;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Forms SDK error
|
|
177
|
+
*/
|
|
178
|
+
declare class FormsError extends Error {
|
|
179
|
+
code: string;
|
|
180
|
+
statusCode: number;
|
|
181
|
+
retryAfter?: number | undefined;
|
|
182
|
+
constructor(message: string, code: string, statusCode: number, retryAfter?: number | undefined);
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Validation error class
|
|
186
|
+
*/
|
|
187
|
+
declare class FormValidationError extends Error {
|
|
188
|
+
errors: ValidationError[];
|
|
189
|
+
constructor(errors: ValidationError[]);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* API client for forms backend
|
|
194
|
+
*/
|
|
195
|
+
declare class FormsApiClient {
|
|
196
|
+
private baseUrl;
|
|
197
|
+
private apiKey;
|
|
198
|
+
private resourceId;
|
|
199
|
+
constructor(config: FormsSDKConfig);
|
|
200
|
+
/**
|
|
201
|
+
* Build URL with token query parameter
|
|
202
|
+
*/
|
|
203
|
+
private buildUrl;
|
|
204
|
+
/**
|
|
205
|
+
* Make an API request
|
|
206
|
+
*/
|
|
207
|
+
private request;
|
|
208
|
+
/**
|
|
209
|
+
* Check if form is active and get configuration
|
|
210
|
+
*/
|
|
211
|
+
isActive(slug: string): Promise<FormStatusResponse>;
|
|
212
|
+
/**
|
|
213
|
+
* Validate form data without submitting
|
|
214
|
+
*/
|
|
215
|
+
validate(slug: string, data: Record<string, unknown>): Promise<ValidationResponse>;
|
|
216
|
+
/**
|
|
217
|
+
* Submit form data (supports files)
|
|
218
|
+
*/
|
|
219
|
+
submit(slug: string, data: Record<string, unknown>, options?: SubmitOptions): Promise<SubmissionResponse>;
|
|
220
|
+
/**
|
|
221
|
+
* Submit with FormData (for file uploads with progress tracking)
|
|
222
|
+
*/
|
|
223
|
+
private submitWithFormData;
|
|
224
|
+
/**
|
|
225
|
+
* Track a form view (for analytics completion rate)
|
|
226
|
+
*/
|
|
227
|
+
trackView(slug: string): Promise<void>;
|
|
228
|
+
/**
|
|
229
|
+
* Get resource ID
|
|
230
|
+
*/
|
|
231
|
+
getResourceId(): string;
|
|
232
|
+
/**
|
|
233
|
+
* Get base URL
|
|
234
|
+
*/
|
|
235
|
+
getBaseUrl(): string;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Form handler for a specific form
|
|
240
|
+
*/
|
|
241
|
+
declare class FormHandler {
|
|
242
|
+
private apiClient;
|
|
243
|
+
private slug;
|
|
244
|
+
private config;
|
|
245
|
+
private options;
|
|
246
|
+
constructor(apiClient: FormsApiClient, slug: string, options?: FormHandlerOptions);
|
|
247
|
+
/**
|
|
248
|
+
* Initialize form handler and fetch configuration
|
|
249
|
+
*/
|
|
250
|
+
initialize(): Promise<FormStatusResponse>;
|
|
251
|
+
/**
|
|
252
|
+
* Get cached form configuration
|
|
253
|
+
*/
|
|
254
|
+
getConfig(): FormStatusResponse | null;
|
|
255
|
+
/**
|
|
256
|
+
* Check if form is active
|
|
257
|
+
*/
|
|
258
|
+
isActive(): boolean;
|
|
259
|
+
/**
|
|
260
|
+
* Check if captcha is required
|
|
261
|
+
*/
|
|
262
|
+
requiresCaptcha(): boolean;
|
|
263
|
+
/**
|
|
264
|
+
* Get captcha provider
|
|
265
|
+
*/
|
|
266
|
+
getCaptchaProvider(): 'turnstile' | 'recaptcha' | 'hcaptcha' | undefined;
|
|
267
|
+
/**
|
|
268
|
+
* Get form schema
|
|
269
|
+
*/
|
|
270
|
+
getSchema(): FormSchema | undefined;
|
|
271
|
+
/**
|
|
272
|
+
* Validate form data
|
|
273
|
+
*/
|
|
274
|
+
validate(data: Record<string, unknown>): Promise<ValidationResponse>;
|
|
275
|
+
/**
|
|
276
|
+
* Submit form data
|
|
277
|
+
*/
|
|
278
|
+
submit(data: Record<string, unknown>, options?: SubmitOptions): Promise<SubmissionResponse>;
|
|
279
|
+
/**
|
|
280
|
+
* Get success message from config
|
|
281
|
+
*/
|
|
282
|
+
getSuccessMessage(): string;
|
|
283
|
+
/**
|
|
284
|
+
* Get redirect URL from config
|
|
285
|
+
*/
|
|
286
|
+
getRedirectUrl(): string | undefined;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Main Forms SDK class
|
|
290
|
+
*/
|
|
291
|
+
declare class FormsSDK {
|
|
292
|
+
private apiClient;
|
|
293
|
+
constructor(config: FormsSDKConfig);
|
|
294
|
+
/**
|
|
295
|
+
* Check if form is active and get configuration
|
|
296
|
+
*/
|
|
297
|
+
isActive(slug: string): Promise<FormStatusResponse>;
|
|
298
|
+
/**
|
|
299
|
+
* Validate form data without submitting
|
|
300
|
+
*/
|
|
301
|
+
validate(slug: string, data: Record<string, unknown>): Promise<ValidationResponse>;
|
|
302
|
+
/**
|
|
303
|
+
* Submit form data
|
|
304
|
+
*/
|
|
305
|
+
submit(slug: string, data: Record<string, unknown>, options?: SubmitOptions): Promise<SubmissionResponse>;
|
|
306
|
+
/**
|
|
307
|
+
* Create a form handler for a specific form
|
|
308
|
+
*/
|
|
309
|
+
form(slug: string, options?: FormHandlerOptions): FormHandler;
|
|
310
|
+
/**
|
|
311
|
+
* Track a form view (for analytics completion rate)
|
|
312
|
+
*/
|
|
313
|
+
trackView(slug: string): Promise<void>;
|
|
314
|
+
/**
|
|
315
|
+
* Submit with retry logic for rate limits
|
|
316
|
+
*/
|
|
317
|
+
submitWithRetry(slug: string, data: Record<string, unknown>, options?: SubmitOptions & {
|
|
318
|
+
maxRetries?: number;
|
|
319
|
+
}): Promise<SubmissionResponse>;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
export { type BasicFieldType, type CaptchaSettings, type FileValidationError, type FormBranding, type FormField, type FormFieldOption, type FormFieldType, FormHandler, type FormHandlerOptions, type FormSchema, type FormStatusResponse, type FormStyling, FormValidationError, FormsApiClient, FormsError, FormsSDK, type FormsSDKConfig, type InteractiveFieldType, type LayoutFieldType, type SubmissionResponse, type SubmitOptions, type UploadProgress, type ValidationError, type ValidationResponse };
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Form field types
|
|
3
|
+
*/
|
|
4
|
+
type BasicFieldType = 'text' | 'email' | 'number' | 'textarea' | 'select' | 'checkbox' | 'file' | 'date' | 'hidden';
|
|
5
|
+
type InteractiveFieldType = 'radio' | 'multiselect' | 'rating' | 'scale' | 'toggle' | 'ranking' | 'imageChoice' | 'phone' | 'url' | 'password' | 'richText' | 'slider' | 'currency' | 'time' | 'datetime' | 'dateRange' | 'address' | 'name' | 'dropdown' | 'colorPicker' | 'location' | 'opinionScale' | 'consent';
|
|
6
|
+
type LayoutFieldType = 'heading' | 'divider' | 'paragraph';
|
|
7
|
+
type FormFieldType = BasicFieldType | InteractiveFieldType | LayoutFieldType;
|
|
8
|
+
interface FormFieldOption {
|
|
9
|
+
label: string;
|
|
10
|
+
value: string;
|
|
11
|
+
imageUrl?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Form field definition
|
|
15
|
+
*/
|
|
16
|
+
interface FormField {
|
|
17
|
+
name: string;
|
|
18
|
+
type: FormFieldType;
|
|
19
|
+
label?: string;
|
|
20
|
+
placeholder?: string;
|
|
21
|
+
required?: boolean;
|
|
22
|
+
options?: string[] | FormFieldOption[];
|
|
23
|
+
defaultValue?: unknown;
|
|
24
|
+
maxFileSize?: number;
|
|
25
|
+
allowedMimeTypes?: string[];
|
|
26
|
+
multiple?: boolean;
|
|
27
|
+
min?: number;
|
|
28
|
+
max?: number;
|
|
29
|
+
step?: number;
|
|
30
|
+
ratingMax?: number;
|
|
31
|
+
lowLabel?: string;
|
|
32
|
+
highLabel?: string;
|
|
33
|
+
defaultCountryCode?: string;
|
|
34
|
+
currencyCode?: string;
|
|
35
|
+
currencySymbol?: string;
|
|
36
|
+
addressFields?: ('street' | 'street2' | 'city' | 'state' | 'zip' | 'country')[];
|
|
37
|
+
nameFields?: ('prefix' | 'first' | 'middle' | 'last' | 'suffix')[];
|
|
38
|
+
content?: string;
|
|
39
|
+
consentText?: string;
|
|
40
|
+
consentUrl?: string;
|
|
41
|
+
maxLength?: number;
|
|
42
|
+
stepId?: string;
|
|
43
|
+
visibleWhen?: {
|
|
44
|
+
field: string;
|
|
45
|
+
operator: 'eq' | 'neq' | 'contains' | 'gt' | 'lt';
|
|
46
|
+
value: unknown;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Form styling configuration
|
|
51
|
+
*/
|
|
52
|
+
interface FormStyling {
|
|
53
|
+
theme: 'light' | 'dark' | 'system';
|
|
54
|
+
primaryColor: string;
|
|
55
|
+
backgroundColor: string;
|
|
56
|
+
textColor: string;
|
|
57
|
+
borderRadius: 'none' | 'sm' | 'md' | 'lg';
|
|
58
|
+
fontSize: 'sm' | 'md' | 'lg';
|
|
59
|
+
buttonStyle: 'filled' | 'outline';
|
|
60
|
+
labelPosition: 'top' | 'left' | 'floating';
|
|
61
|
+
customCss?: string;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Form schema
|
|
65
|
+
*/
|
|
66
|
+
interface FormSchema {
|
|
67
|
+
fields: FormField[];
|
|
68
|
+
styling?: FormStyling;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Form captcha settings
|
|
72
|
+
*/
|
|
73
|
+
interface CaptchaSettings {
|
|
74
|
+
enabled: boolean;
|
|
75
|
+
provider?: 'turnstile' | 'recaptcha' | 'hcaptcha';
|
|
76
|
+
siteKey?: string;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Form branding configuration
|
|
80
|
+
*/
|
|
81
|
+
interface FormBranding {
|
|
82
|
+
enabled: boolean;
|
|
83
|
+
text?: string;
|
|
84
|
+
url?: string;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Form status response from API
|
|
88
|
+
*/
|
|
89
|
+
interface FormStatusResponse {
|
|
90
|
+
active: boolean;
|
|
91
|
+
formId?: string;
|
|
92
|
+
name?: string;
|
|
93
|
+
mode?: 'free' | 'schema';
|
|
94
|
+
schema?: FormSchema;
|
|
95
|
+
error?: string;
|
|
96
|
+
settings?: {
|
|
97
|
+
captcha: CaptchaSettings;
|
|
98
|
+
honeypot: boolean;
|
|
99
|
+
allowAttachments: boolean;
|
|
100
|
+
maxAttachments?: number;
|
|
101
|
+
maxAttachmentSize?: number;
|
|
102
|
+
successMessage?: string;
|
|
103
|
+
redirectUrl?: string;
|
|
104
|
+
};
|
|
105
|
+
/** Branding configuration */
|
|
106
|
+
branding?: FormBranding;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Validation error
|
|
110
|
+
*/
|
|
111
|
+
interface ValidationError {
|
|
112
|
+
field: string;
|
|
113
|
+
message: string;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Validation response from API
|
|
117
|
+
*/
|
|
118
|
+
interface ValidationResponse {
|
|
119
|
+
valid: boolean;
|
|
120
|
+
errors: ValidationError[];
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Submission response from API
|
|
124
|
+
*/
|
|
125
|
+
interface SubmissionResponse {
|
|
126
|
+
success: boolean;
|
|
127
|
+
submissionId: string;
|
|
128
|
+
message: string;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Submit options
|
|
132
|
+
*/
|
|
133
|
+
interface SubmitOptions {
|
|
134
|
+
pageUrl?: string;
|
|
135
|
+
captchaToken?: string;
|
|
136
|
+
/** Callback for upload progress */
|
|
137
|
+
onProgress?: (progress: UploadProgress) => void;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Upload progress information
|
|
141
|
+
*/
|
|
142
|
+
interface UploadProgress {
|
|
143
|
+
loaded: number;
|
|
144
|
+
total: number;
|
|
145
|
+
percentage: number;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* File validation error
|
|
149
|
+
*/
|
|
150
|
+
interface FileValidationError {
|
|
151
|
+
field: string;
|
|
152
|
+
file: string;
|
|
153
|
+
error: 'size' | 'type' | 'count';
|
|
154
|
+
message: string;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* SDK configuration
|
|
158
|
+
*/
|
|
159
|
+
interface FormsSDKConfig {
|
|
160
|
+
apiKey: string;
|
|
161
|
+
resourceId: string;
|
|
162
|
+
baseUrl?: string;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Form handler options
|
|
166
|
+
*/
|
|
167
|
+
interface FormHandlerOptions {
|
|
168
|
+
/** Track form views for analytics (completion rate) */
|
|
169
|
+
trackViews?: boolean;
|
|
170
|
+
onSubmitStart?: () => void;
|
|
171
|
+
onSubmitSuccess?: (response: SubmissionResponse) => void;
|
|
172
|
+
onSubmitError?: (error: FormsError) => void;
|
|
173
|
+
onValidationError?: (errors: ValidationError[]) => void;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Forms SDK error
|
|
177
|
+
*/
|
|
178
|
+
declare class FormsError extends Error {
|
|
179
|
+
code: string;
|
|
180
|
+
statusCode: number;
|
|
181
|
+
retryAfter?: number | undefined;
|
|
182
|
+
constructor(message: string, code: string, statusCode: number, retryAfter?: number | undefined);
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Validation error class
|
|
186
|
+
*/
|
|
187
|
+
declare class FormValidationError extends Error {
|
|
188
|
+
errors: ValidationError[];
|
|
189
|
+
constructor(errors: ValidationError[]);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* API client for forms backend
|
|
194
|
+
*/
|
|
195
|
+
declare class FormsApiClient {
|
|
196
|
+
private baseUrl;
|
|
197
|
+
private apiKey;
|
|
198
|
+
private resourceId;
|
|
199
|
+
constructor(config: FormsSDKConfig);
|
|
200
|
+
/**
|
|
201
|
+
* Build URL with token query parameter
|
|
202
|
+
*/
|
|
203
|
+
private buildUrl;
|
|
204
|
+
/**
|
|
205
|
+
* Make an API request
|
|
206
|
+
*/
|
|
207
|
+
private request;
|
|
208
|
+
/**
|
|
209
|
+
* Check if form is active and get configuration
|
|
210
|
+
*/
|
|
211
|
+
isActive(slug: string): Promise<FormStatusResponse>;
|
|
212
|
+
/**
|
|
213
|
+
* Validate form data without submitting
|
|
214
|
+
*/
|
|
215
|
+
validate(slug: string, data: Record<string, unknown>): Promise<ValidationResponse>;
|
|
216
|
+
/**
|
|
217
|
+
* Submit form data (supports files)
|
|
218
|
+
*/
|
|
219
|
+
submit(slug: string, data: Record<string, unknown>, options?: SubmitOptions): Promise<SubmissionResponse>;
|
|
220
|
+
/**
|
|
221
|
+
* Submit with FormData (for file uploads with progress tracking)
|
|
222
|
+
*/
|
|
223
|
+
private submitWithFormData;
|
|
224
|
+
/**
|
|
225
|
+
* Track a form view (for analytics completion rate)
|
|
226
|
+
*/
|
|
227
|
+
trackView(slug: string): Promise<void>;
|
|
228
|
+
/**
|
|
229
|
+
* Get resource ID
|
|
230
|
+
*/
|
|
231
|
+
getResourceId(): string;
|
|
232
|
+
/**
|
|
233
|
+
* Get base URL
|
|
234
|
+
*/
|
|
235
|
+
getBaseUrl(): string;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Form handler for a specific form
|
|
240
|
+
*/
|
|
241
|
+
declare class FormHandler {
|
|
242
|
+
private apiClient;
|
|
243
|
+
private slug;
|
|
244
|
+
private config;
|
|
245
|
+
private options;
|
|
246
|
+
constructor(apiClient: FormsApiClient, slug: string, options?: FormHandlerOptions);
|
|
247
|
+
/**
|
|
248
|
+
* Initialize form handler and fetch configuration
|
|
249
|
+
*/
|
|
250
|
+
initialize(): Promise<FormStatusResponse>;
|
|
251
|
+
/**
|
|
252
|
+
* Get cached form configuration
|
|
253
|
+
*/
|
|
254
|
+
getConfig(): FormStatusResponse | null;
|
|
255
|
+
/**
|
|
256
|
+
* Check if form is active
|
|
257
|
+
*/
|
|
258
|
+
isActive(): boolean;
|
|
259
|
+
/**
|
|
260
|
+
* Check if captcha is required
|
|
261
|
+
*/
|
|
262
|
+
requiresCaptcha(): boolean;
|
|
263
|
+
/**
|
|
264
|
+
* Get captcha provider
|
|
265
|
+
*/
|
|
266
|
+
getCaptchaProvider(): 'turnstile' | 'recaptcha' | 'hcaptcha' | undefined;
|
|
267
|
+
/**
|
|
268
|
+
* Get form schema
|
|
269
|
+
*/
|
|
270
|
+
getSchema(): FormSchema | undefined;
|
|
271
|
+
/**
|
|
272
|
+
* Validate form data
|
|
273
|
+
*/
|
|
274
|
+
validate(data: Record<string, unknown>): Promise<ValidationResponse>;
|
|
275
|
+
/**
|
|
276
|
+
* Submit form data
|
|
277
|
+
*/
|
|
278
|
+
submit(data: Record<string, unknown>, options?: SubmitOptions): Promise<SubmissionResponse>;
|
|
279
|
+
/**
|
|
280
|
+
* Get success message from config
|
|
281
|
+
*/
|
|
282
|
+
getSuccessMessage(): string;
|
|
283
|
+
/**
|
|
284
|
+
* Get redirect URL from config
|
|
285
|
+
*/
|
|
286
|
+
getRedirectUrl(): string | undefined;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Main Forms SDK class
|
|
290
|
+
*/
|
|
291
|
+
declare class FormsSDK {
|
|
292
|
+
private apiClient;
|
|
293
|
+
constructor(config: FormsSDKConfig);
|
|
294
|
+
/**
|
|
295
|
+
* Check if form is active and get configuration
|
|
296
|
+
*/
|
|
297
|
+
isActive(slug: string): Promise<FormStatusResponse>;
|
|
298
|
+
/**
|
|
299
|
+
* Validate form data without submitting
|
|
300
|
+
*/
|
|
301
|
+
validate(slug: string, data: Record<string, unknown>): Promise<ValidationResponse>;
|
|
302
|
+
/**
|
|
303
|
+
* Submit form data
|
|
304
|
+
*/
|
|
305
|
+
submit(slug: string, data: Record<string, unknown>, options?: SubmitOptions): Promise<SubmissionResponse>;
|
|
306
|
+
/**
|
|
307
|
+
* Create a form handler for a specific form
|
|
308
|
+
*/
|
|
309
|
+
form(slug: string, options?: FormHandlerOptions): FormHandler;
|
|
310
|
+
/**
|
|
311
|
+
* Track a form view (for analytics completion rate)
|
|
312
|
+
*/
|
|
313
|
+
trackView(slug: string): Promise<void>;
|
|
314
|
+
/**
|
|
315
|
+
* Submit with retry logic for rate limits
|
|
316
|
+
*/
|
|
317
|
+
submitWithRetry(slug: string, data: Record<string, unknown>, options?: SubmitOptions & {
|
|
318
|
+
maxRetries?: number;
|
|
319
|
+
}): Promise<SubmissionResponse>;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
export { type BasicFieldType, type CaptchaSettings, type FileValidationError, type FormBranding, type FormField, type FormFieldOption, type FormFieldType, FormHandler, type FormHandlerOptions, type FormSchema, type FormStatusResponse, type FormStyling, FormValidationError, FormsApiClient, FormsError, FormsSDK, type FormsSDKConfig, type InteractiveFieldType, type LayoutFieldType, type SubmissionResponse, type SubmitOptions, type UploadProgress, type ValidationError, type ValidationResponse };
|