import { ContentItem, PopupModal, SubmitError, SubmitSuccess, SubmitTarget } from '../schema/index.ts'; /** * Live form values, keyed by content item id. Most inputs hold a string, a * checkbox holds a boolean, and a `multi-select` holds the list of chosen * option values. */ export type FormValue = string | boolean | string[]; export type FormValues = Record; /** * Campaign/attribution params a submit forwards on its own, without the author * having to add a field for them. These are the keys ad platforms and mail * tools hang on a landing page URL, so a form opened on that page can pass the * attribution through to wherever it submits. * * The list is fixed rather than "forward the whole query string" on purpose: a * host's page URLs also carry session tokens, order ids and sometimes an email * address, and none of that should travel to the form's endpoint unasked. */ export declare const TRACKING_KEYS: string[]; export interface SubmitOutcome { ok: boolean; success?: SubmitSuccess; error?: SubmitError; /** Resolved coupon code, when onSuccess is a (legacy) coupon. */ couponCode?: string; /** * The primary target's parsed JSON response, when it had one. A `rich` success * screen may embed several coupon components, each with its own response path, * so the renderer needs the raw response to resolve them at render time — * unlike the single legacy `couponCode`, which is resolved here. */ responseJson?: unknown; } /** Read a dot-path (e.g. "data.coupon") out of a parsed JSON response. */ export declare function readPath(obj: unknown, path: string): unknown; export interface AssembledRequest { url: string; method: 'GET' | 'POST'; headers: Record; body?: string; } /** * Build the outgoing request for one submit target from the popup's input items * and current values. Every target reuses the same field values but routes them * by its *own* method (GET → query string, POST → JSON body) and merges its own * static `payload`. */ export declare function assembleTargetRequest(target: SubmitTarget, popup: PopupModal, values: FormValues): AssembledRequest; /** * Build the outgoing request for the *primary* target — the popup's own `url` / * `method` / `onSubmitCallbackPayload`. Kept as the historical entry point; * {@link assembleTargetRequest} generalizes it to any target. */ export declare function assembleRequest(popup: PopupModal, values: FormValues): AssembledRequest; /** * Append the submitted field values to a URL as query params, keyed by each * field's submit key. Used on redirect success so the destination page (e.g. a * thank-you page) can read them — greet the visitor by name, and so on. Existing * query params on the URL are preserved; empty fields are skipped. */ export declare function appendValuesToUrl(popup: PopupModal, values: FormValues, url: string): string; /** Validate required inputs. Returns the id of the first offending item, or null. */ export declare function firstMissingRequired(popup: PopupModal, values: FormValues, scope?: readonly ContentItem[]): string | null; /** Basic email shape: something@something.tld, no whitespace. */ export declare const EMAIL_RE: RegExp; /** * Validate the format of any filled-in email inputs. Empty values are ignored * here — emptiness is only an error when the field is required (see * {@link firstMissingRequired}). Returns the id of the first malformed email. */ export declare function firstInvalidEmail(popup: PopupModal, values: FormValues, scope?: readonly ContentItem[]): string | null; /** * Deliberately permissive phone shape: digits with the punctuation people * actually type (`+ ( ) - . space`), at least 6 digits in total. Phone formats * differ by country, so this only catches the obviously-not-a-number case * rather than trying to be an authority on national numbering plans. */ export declare const TEL_RE: RegExp; /** * Validate the format of any filled-in phone inputs. Like * {@link firstInvalidEmail}, empty values are left to the required check. */ export declare function firstInvalidTel(popup: PopupModal, values: FormValues, scope?: readonly ContentItem[]): string | null; /** * Validate any filled-in number inputs against their `min`/`max`. Like the email * and phone checks, an empty value is left to the required check — a blank * optional field isn't out of range, it's unanswered. * * The bounds are on the input as attributes too, but a visitor can type a value * the steppers would never have reached, and a `private` number never renders at * all: its value is seeded from the page URL, where nothing has been checked. * Returns the id of the first offending item, with which end it broke. */ export declare function firstOutOfRange(popup: PopupModal, values: FormValues, scope?: readonly ContentItem[]): { id: string; min?: number; max?: number; } | null; /** * Submit the popup form to every configured endpoint and derive the post-submit * behavior. The primary target (index 0 of {@link effectiveTargets}) is * authoritative: its HTTP status decides success/error and its response drives * the coupon path. Host-added extra targets — e.g. a mailing-list automation — * fire in parallel and best-effort, so their failures never surface to the * visitor or cost the lead the primary target already captured. * * Targets marked `fireFromClient: false` are skipped: the host reads those off * the stored form and acts on them when the primary submission reaches its * backend. A form whose automations are all declared that way costs the visitor * exactly one request. */ export declare function submitPopup(popup: PopupModal, values: FormValues, fetchImpl?: typeof fetch): Promise;