import { RoxyLocalizedElement } from '../i18n/localized-element.js'; /** * Drop every cached spec promise. * @remarks * Test-only, and deliberately NOT re-exported from the package entry. The cache is module state shared by every test file in a single `bun test` process, and it only self-evicts on rejection, so one file that resolves a spec leaves a warm entry that silently satisfies a later file assertion about fetching. That made slice-fallback coverage pass or fail purely on file order. * @internal */ export declare function resetSpecCache(): void; /** * Schema-driven form. Pass `endpoint` (e.g. "vedic-astrology/birth-chart"). The form digests the operation's request schema into a spec-derived input for each parameter (a zodiac tile grid, a date or time input, a city search, a toggle) with progressive disclosure, and emits a `roxy-submit` CustomEvent carrying the validated payload. The caller decides what to do (call the SDK, render a chart, navigate). * * @remarks * Schema resolution order: an explicit `spec-url` fetches that full spec and digests it (unchanged, the demo path); otherwise the form tries a small version-pinned per-operation slice from the CDN, and on any miss falls back to fetching the production spec. Each input kind is chosen purely from the parameter shape (see {@link ../utils/field-schema.ts}), so a new endpoint gets a working, on-brand form with no per-endpoint code. * * The visitor-facing `lang` parameter is never rendered: a site owner sets the element `lang` attribute, and the form routes it to the query string on submit. Optional parameters collapse under one Advanced disclosure; a form whose only required field is an enum submits on selection. * * **Two languages are in play here and they are different answers.** {@link RoxyLocalizedElement.effectiveLang} is the DISPLAY tag, region included, and it is what every `t()` call and the city search read. {@link RoxyEndpointForm.requestLang} is the WIRE value, region stripped and unsupported languages omitted, and it is what reaches `?lang=`. Swapping them is silent in both directions: the request one demotes every regional visitor, and the display one is a 400. * * **Field labels and option text ARE translated now, and they arrive over the network rather than from a catalogue.** They are keyed by the WIRE NAME the spec uses, not by the English text, because that English text is itself computed here by `humanize()`: there is no constant for a source-keyed catalogue to key on, which is why the chrome catalogue could never reach them. {@link RoxyEndpointForm.fieldText} and {@link RoxyEndpointForm.optionText} read a map baked into the per-language payload at build time and published through the same registry the chrome strings use. It rides the `locales/{lang}.js` download a translated page already makes, so it costs no extra request, needs no network at render time, and cannot flash English before correcting itself. * * **`humanize()` remains, as the fallback, and that is what makes this safe.** The payload is cosmetic and never awaited: a form draws immediately, an offline page or a blocked request renders exactly what it rendered before labels existed, and a language with no translation for a given key falls back to English server-side. There is no state in which a missing label breaks a form. Operation SUMMARIES and field DESCRIPTIONS are deliberately NOT in that map: a description is developer-facing reference prose, most of it collapsed behind a disclosure, and the widget already states the same contract language-neutrally through its input type and bounds. GROUP names stay catalogued rather than fetched, because the spec has nine of them: see {@link RoxyEndpointForm.groupName}. */ export declare class RoxyEndpointForm extends RoxyLocalizedElement { static styles: import("lit").CSSResult[]; endpoint: string; method: 'GET' | 'POST'; /** Explicit OpenAPI spec URL. When set, the form fetches and digests that full spec (the demo path). When empty, it resolves a version-pinned slice first, then the production spec. */ specUrl: string; /** Override the submit-button label. Empty derives an outcome-first label from the endpoint. */ submitLabel: string; /** Browser-safe publishable key, forwarded to the slotted city search so the natal or synastry form can geocode. */ publishableKey?: string; /** Where the slotted city search sends its request, absolute or relative to the page. Set it when the page routes its API traffic through its own server, so the one request this form issues on its own reaches that server too. Unset, the search keeps its own default. */ locationUrl?: string; /** Prefill values, keyed by field name (nested per group). Used by `remember` mode and to restore the previous submission. JS property only. */ initialValues?: Record; private fields; private formTitle; private hasLang; private values; private loaded; private specError; private validationErrors; connectedCallback(): void; private loadSchema; /** The failure sentence a visitor reads. Composed here rather than at the throw, so the shared spec-cache promise cannot freeze one form's language onto another. A message from anywhere else (a browser network error) is a wire fact and passes through untranslated. */ private specErrorMessage; /** Resolve the form model: explicit spec-url wins; else a version-pinned slice; else the production spec. */ private resolveModel; /** Fetch and digest one operation from a full OpenAPI document. */ private modelFromSpec; /** Try the small precomputed slice; return null on any miss so the caller falls back to the full spec. */ private tryLoadSlice; private applyModel; private retryLoadSchema; private setValue; /** The field's render role: a hidden autogenerated seed, a hidden defaulted limit/offset, a suppressed location coordinate, or a normally rendered input. */ private roleOf; /** True when the field renders its own input (as opposed to being hidden or replaced by the city search). */ private isRendered; /** Ordered, de-duplicated group keys (undefined flat group plus each named object). */ private groupKeys; /** True when the fields in `group` (or the flat top level) carry a latitude+longitude pair, so a location-search can autofill them. Timezone is NOT required here, see {@link LOCATION_PAIR}. */ private groupHasLocation; /** * Location groups in field order. Order is load-bearing: when a request carries coordinate * groups but only ONE unprefixed top-level `timezone`, the FIRST group owns it, because that * timezone belongs to the primary moment (`generateRelocationChart` has one `timezone` and it is * the birth timezone). Without an owner both city boxes would write the same key and the second * pick would silently overwrite the first with the wrong offset. */ private locationGroups; /** * The timezone field a group's city search should fill, or undefined when there is none. * * Prefers a timezone inside the group (the nested `person1`/`person2` shape has its own). Falls * back to an unowned flat `timezone` for the first location group only, which is what makes the * prefixed shape work without leaving a decimal-hours box for a visitor to guess at. */ private timezoneFieldFor; /** * True when the location block must show a required mark, i.e. ANY member of the trio is required. The block is a single city-search input that fills all three, so if even one is required (e.g. bodygraph requires `timezone` while `latitude`/`longitude` are optional) the input is required and `collectMissing` blocks submit without it. Requiring ALL three understated that: the asterisk went missing on a block the form still enforced, which reads as optional to a non-technical embedder. */ private locationRequired; /** A named group is a required input when any of its leaf fields is required or it carries a location. */ private groupIsRequired; /** * The single visible required enum field, when the whole form reduces to exactly one: no location block, no named group, one required rendered enum. This is what turns the horoscope form into a tap-to-load sign grid; returns null otherwise. */ private get singleEnumField(); /** The submit-button label. A caller-supplied one is theirs and is printed verbatim; the derived verb is ours, so it goes through the catalogue. {@link deriveSubmitLabel} returns the canonical English verb, which IS the catalogue key. */ private effectiveSubmitLabel; /** * The value this form may put on `?lang=`, or undefined to leave the request on the API default. * * @remarks * NOT the display language, and the name says so: `effectiveLang()` on the base class is the full display tag every `t()` reads. `?lang=es-AR` is a 400 and `?lang=es` is a 200, and the language resolves from `` rather than only from an attribute a developer typed, so the unfiltered tag reaching the query string would break every regional and every untranslated locale. {@link apiLang} does both narrowings; this method exists so the submit path reads as one call. */ private requestLang; /** * The words this form writes over a request field, and the options under it. * * @remarks * `humanize()` is the FALLBACK, not the default, and it stays because it has to: the labels * arrive over the network and a form must draw before they land, on an offline page, and when * the request fails. So the worst case is exactly what this form rendered before labels * existed, and the best case is a translated one, with no state in between where it breaks. * * Keyed by WIRE NAME rather than by the English text, because the English text is itself * computed here by `humanize()`; there is no constant to key on. Same reason the label map is * a separate key space from the chrome catalogue. */ private fieldText; /** The option text under one field, falling back the same way. */ private optionText; /** * Location-select handler bound to a group: fills that group's coordinate keys plus whichever * timezone field it owns. * * Keys are LOOKED UP from the model rather than built as `group.name`. That assumption held only * while every group came from object nesting; a prefixed group stores under the original wire * name (`birthLatitude`), so constructing `birth.latitude` would write a key the request builder * never reads and the coordinates would silently stay empty. */ private onLocationFor; private selectTile; /** Roving-tabindex arrow-key navigation for a tile radiogroup, modeled on the shared tablist pattern (selection follows focus). */ private onTilesKeyDown; private onSubmit; /** Validate, then emit `roxy-submit` with the reconstructed payload, the spec query keys, and whether the form was a single-enum sticky picker. */ private submit; /** Required fields left empty, with the location trio collapsed to one entry per group. Seed and hidden defaults never block. */ private collectMissing; private reqMark; /** A description under a field: rendered inline when short, collapsed behind a disclosure showing its first line when long. */ private description; private renderTiles; private renderSelect; private renderToggle; private renderInput; private renderField; /** * The fields this group's city search actually fills, named in the help text. * * Not the hardcoded "latitude, longitude, timezone" it used to say: a group does not always own a * timezone. `generateRelocationChart` has one top-level `timezone` that belongs to the birth * moment, so the relocation block fills coordinates only, and promising a timezone there would be * a visible lie on the one form that made this method necessary. */ private locationFillList; /** * The name of one request group, translated, falling back to the humanized English. * * @remarks * A group name is the one derived label that is also ENUMERABLE. A field label is `humanize()` over one of 909 spec field names, but a group is `humanize()` over the names an object-valued property or a coordinate prefix can take, and the whole spec has nine across 176 operations. So each is a catalogue entry, and the English token that used to sit inside translated prose (`Local de Natal Chart`, `Место (Birth Data)`) is gone. A tenth group appearing in the spec still renders: `t()` returns its source string on a miss, so it degrades to the humanized English rather than to a blank or a key. * * **The lookup folds case, and that is what makes `natalChart` work.** `humanize` produces `Natal Chart` while the catalogue carries `Natal chart` for the card heading, and {@link lookupKey} folds both to one key on write and on read, so this reuses the shipped translation. Adding the capitalized twin instead would SILENTLY OVERWRITE that heading in every locale rather than reading as a duplicate, which is why `i18n/chrome-strings.ts` carries no entry for it. */ private groupName; /** * The name of one location block, and of the same block in a validation message, which is why it is a method rather than two literals. * * The placeholder carries the group rather than the form concatenating a name in front of a translated noun, so a translator owns the word order: `Person 1 location` is English syntax, and Turkish suffixes the head noun, Hindi takes a genitive, Russian a parenthetical and German a colon. The four shapes are deliberately different and normalizing them breaks three languages. */ private locationLabel; /** * One city search standing in for a group's raw coordinates. * * `lang` is forwarded EXPLICITLY and that line is load-bearing: the city search lives inside this shadow root, and the `closest('[lang]')` link of the resolution chain stops at a shadow boundary, so without it the dropdown renders its own empty state and its refusal message in English on a fully translated page. Every composing component carries the same obligation. * * `endpoint` is forwarded for the same structural reason as the key. This search is the one request the form issues on its own, it sits inside this shadow root where a host page cannot reach it, and it is the only part of a birth-data form that talks to the API before submit. So a page that routes its API traffic through its own server states that route once, on the form, and it lands here. An unset {@link RoxyEndpointForm.locationUrl} omits the attribute entirely, which leaves the search on its own default. */ private locationBlock; private groupCard; render(): import("lit").TemplateResult<1>; private coerce; } declare global { interface HTMLElementTagNameMap { 'roxy-endpoint-form': RoxyEndpointForm; } } //# sourceMappingURL=endpoint-form.d.ts.map