/** * Supported ISO 3166-1 alpha-2 country codes. */ type CountryCode = 'NL' | 'DE' | 'BE' | 'AT' | 'DK' | 'CH' | 'LU' | 'FR' | 'ES' | 'GB' | (string & {}); /** * Supported language codes for address labels. */ type LanguageCode = 'nl' | 'fr' | 'de' | (string & {}); /** * The ordering applied to mixed address and place suggestions. */ type SortOrder = 'relevance' | 'distance'; /** * The kinds of suggestions requested from the Infer API. * * When omitted, the API keeps its address-only default. */ type InferTypes = 'address' | 'place' | 'address,place'; /** * The current step in the address inference process. * - `empty`: No input yet. * - `mixed`: User is prompted to choose between cities and streets. * - `place`: User is selecting a point of interest. * - `street`: User is selecting a street. * - `city`: User is selecting a city. * - `postcode`: User is entering a postcode. * - `street_number`: User is entering a street number. * - `street_number_first`: Specialized mode where number is entered before street. * - `addition`: Selecting a street number addition (e.g., 'A', 'III'). * - `direct`: Direct address hit (often via postcode). * - `final`: A complete, valid address has been identified. */ type Stage = 'empty' | 'mixed' | 'place' | 'street' | 'city' | 'postcode' | 'street_number' | 'street_number_first' | 'addition' | 'direct' | 'final'; /** * The standardized address object returned upon a successful final selection. */ interface AddressValue { /** The name of the street. */ street: string; /** The name of the city/locality. */ city: string; /** Latitude of the address location. */ lat?: number | null; /** Longitude of the address location. */ lng?: number | null; /** The street number. */ street_number?: string | number; /** The postal code. */ postcode?: string; /** The street number addition or suffix. */ addition?: string | null; /** Allow for extra fields if API expands. */ [key: string]: unknown; } /** * A point of interest returned by the Infer API. */ interface PlaceValue { /** Stable identifier of the place in the source dataset. */ place_id: string; /** Display name of the place. */ name: string; /** Primary place category. */ category: string; /** Human-readable address, when available. */ formatted_address: string | null; /** Address components, when available. */ street: string | null; street_number: number | null; addition: string | null; postcode: string | null; city: string | null; /** Coordinates of the place. */ lat: number; lng: number; } /** Shared display fields for a suggestion in the result list. */ interface InferResultBase { /** The text to display in the UI (e.g. "Main Street"). */ label: string; /** Secondary information (e.g. city name when suggesting a street). */ subtitle?: string | null; /** Number of underlying results found for this suggestion. */ count?: number | string; /** False for close (fuzzy) matches that top up the exact matches. */ exact?: boolean; /** The lowercase name the fuzzy pass matched, for close matches. */ matched_value?: string | null; /** 0-based query positions that differ from the match, for close matches. */ diff_positions?: number[] | null; } /** A regular address or partial-address suggestion. */ interface AddressSuggestion extends InferResultBase { /** The actual address data, when this result completes an address. */ value?: AddressValue | string; /** Address suggestions may omit this field, as in existing API responses. */ type?: 'address'; } /** A suggestion that represents a point of interest rather than an address. */ interface PlaceSuggestion extends InferResultBase { type: 'place'; value: PlaceValue; } /** A single item in the suggestion list. */ type InferResult = AddressSuggestion | PlaceSuggestion; /** * The complete UI state managed by InferCore. */ interface InferState { /** The current text value of the search input. */ query: string; /** The current logical stage of the address lookup. */ stage: Stage | null; /** List of city suggestions (used in `mixed` stage). */ cities: InferResult[]; /** List of street suggestions (used in `mixed` stage). */ streets: InferResult[]; /** General list of suggestions for the current stage. */ suggestions: InferResult[]; /** Place suggestions returned as a separate API partition, when provided. */ places: PlaceSuggestion[]; /** Flag indicating if the current selection is complete and valid. */ isValid: boolean; /** The selected address value, otherwise null (places use `selectedPlace`). */ value: AddressValue | null; /** The selected place object, otherwise null. */ selectedPlace: PlaceValue | null; /** Flag indicating if the last API request failed. */ isError: boolean; /** Flag indicating if a network request is currently in progress. */ isLoading: boolean; /** Flag indicating if more results are available to load. */ hasMore: boolean; /** * The index of the currently highlighted suggestion. * - `0` to `n`: An item is highlighted via keyboard navigation. * - `-1`: No item is highlighted. */ selectedSuggestionIndex: number; } /** * Custom fetch implementation, compatible with the Web Fetch API. * Useful for Node.js environments or proxying requests. */ type Fetcher = (input: RequestInfo | URL, init?: RequestInit) => Promise; /** * Configuration options for the Infer engine. */ interface InferConfig { /** * Your Pro6PP Authorization Key. * Optional if using a proxy. */ authKey?: string; /** * The country to perform address lookups in. */ country: CountryCode; /** * * If provided, this URL is used as the API endpoint (query params will be appended). * * If not provided, the SDK defaults to 'https://api.pro6pp.nl/v2/infer/{country}'. */ apiUrl?: string; /** * Custom fetch implementation for network requests. * @default window.fetch */ fetcher?: Fetcher; /** * Number of suggestions to request per batch. * @default 20 */ limit?: number; /** * The delay in milliseconds before performing the API search. * Note: A lower bound of 50ms is enforced to protect API stability. * @default 150 */ debounceMs?: number; /** * Maximum number of retry attempts for transient network errors. * Valid range: 0 to 10. * @default 0 */ maxRetries?: number; /** * Language code for response labels. * Affects the language of returned address labels. * Only applicable for BE country code. */ language?: LanguageCode; /** * Sort mixed address and place suggestions by relevance or distance. * @default 'relevance' */ sort?: SortOrder; /** * Latitude of the origin used for distance sorting. * When provided, `lng` must also be provided. */ lat?: number; /** * Longitude of the origin used for distance sorting. * When provided, `lat` must also be provided. */ lng?: number; /** * Suggestion types to request. Omit this to preserve the address-only default. */ types?: InferTypes; /** * Callback triggered whenever the internal state (suggestions, loading status, etc.) updates. */ onStateChange?: (state: InferState) => void; /** * Callback triggered when a user selects an item. * If the address is complete, returns an `AddressValue` object. * If the selection is partial, returns a `string`. */ onSelect?: (selection: AddressValue | string | null) => void; /** Callback triggered when a point of interest is selected. */ onPlaceSelect?: (selection: PlaceValue | null) => void; } /** * Represents a segment of text that should be highlighted or left plain. */ interface HighlightSegment { text: string; match: boolean; } /** * The initial state of the address inference engine. */ declare const INITIAL_STATE: InferState; /** * Returns suggestions in the order intended for display and keyboard navigation. * Mixed and place responses provide the ranked order in `suggestions`. * If that list is empty, fall back to the separate category arrays. */ declare function getOrderedItems(state: InferState): InferResult[]; /** * The core logic engine for Pro6PP Infer. * Manages API communication, state transitions, and keyboard interaction logic. */ declare class InferCore { private country; private authKey?; private explicitApiUrl?; private baseLimit; private currentLimit; private maxRetries; private language?; private sort?; private lat?; private lng?; private types?; private fetcher; private onStateChange; private onSelect; private onPlaceSelect; /** * The current read-only state of the engine. * Use `onStateChange` to react to updates. */ state: InferState; private abortController; private debouncedFetch; private isDestroyed; /** * Initializes a new instance of the Infer engine. * @param config The configuration object including API keys and callbacks. */ constructor(config: InferConfig); /** * Processes new text input from the user. * Triggers a debounced API request and updates the internal state. * @param value The raw string from the input field. */ handleInput(value: string): void; /** * Increases the current limit and re-fetches the query to show more results. */ loadMore(): void; /** * Handles keyboard events for the input field. * Supports: * - `ArrowUp`/`ArrowDown`: Navigate through the suggestion list. * - `Enter`: Select the currently highlighted suggestion. * - `Space`: Automatically inserts a comma if a numeric street number is detected. * @param event The keyboard event from the input element. */ handleKeyDown(event: KeyboardEvent | { key: string; target: EventTarget | null; preventDefault: () => void; }): void; /** * Manually selects a suggestion or a string value. * This is typically called when a user clicks a suggestion in the UI. * @param item The suggestion object or string to select. * @returns boolean True if the selection is a final address or place. */ selectItem(item: InferResult | string): boolean; /** * Disposes the engine and prevents pending async work from updating state. */ destroy(): void; private shouldAutoInsertComma; private finishSelection; private finishPlaceSelection; private processSelection; private executeFetch; private retry; private mapResponseToState; /** * Reformats a suggestion's label based on the user's input order. * If the suggestion has a structured value object, we reorder the label * to match how the user typed the components. */ private reformatSuggestionLabel; private updateQueryAndFetch; private replaceLastSegment; private getQueryPrefix; private getCurrentFragment; private resetState; private updateState; private debounce; } /** * Splits text into matched and unmatched segments for suggestion rendering. * * The API matches street and city names on any substring of the * accent-folded, lowercased name, so a contiguous occurrence of the folded * query is looked up first and highlighted as one segment: query `gebouw` * highlights the `gebouw` in `Klokgebouw`, and `bazille` highlights * `Bazillé` even though the query carries no accent. * * When the label does not contain the query verbatim (reordered multi-part * queries, fuzzy matches), it falls back to a greedy in-order walk over the * same folded text, placing each folded query character at its first * available position, so accents fold identically on both paths. If even * that fails to place every character, nothing is highlighted. */ declare function getHighlightSegments(text: string, query: string): HighlightSegment[]; /** * Splits text into matched and unmatched segments for a close (fuzzy) match. * * A fuzzy suggestion need not contain the query, so this highlights the * longest common subsequence of the folded query and the folded label: the * characters the two share in order, wherever the typo moved them. Unplaced * query characters are expected and fine; a label sharing nothing with the * query renders unhighlighted. */ declare function getFuzzyHighlightSegments(text: string, query: string): HighlightSegment[]; /** * Formats a label for display based on the user's input order. * Components the user typed appear first (in their order), * followed by components they didn't type (new info from API). * * @param query The user's current query string * @param value The structured address value from the API * @returns A formatted label string */ declare function formatLabelByInputOrder(query: string, value: AddressValue): string; /** * Extracts the address from the API place subtitle. * * The API subtitle is formatted as `category · address`; the SDK only renders * the address because the place name already identifies the result. */ declare function getPlaceAddress(subtitle?: string | null): string; declare const DEFAULT_STYLES = "\n .pro6pp-wrapper {\n position: relative;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n box-sizing: border-box;\n width: 100%;\n -webkit-tap-highlight-color: transparent;\n }\n .pro6pp-wrapper * {\n box-sizing: border-box;\n }\n .pro6pp-input {\n width: 100%;\n padding: 12px 14px;\n padding-right: 48px;\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n font-size: 16px;\n line-height: 1.5;\n appearance: none;\n transition: border-color 0.2s, box-shadow 0.2s;\n }\n\n .pro6pp-input::placeholder {\n font-size: 16px;\n color: #a3a3a3;\n }\n\n .pro6pp-input:focus {\n outline: none;\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);\n }\n\n .pro6pp-input-addons {\n position: absolute;\n right: 4px;\n top: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n pointer-events: none;\n }\n .pro6pp-input-addons > * {\n pointer-events: auto;\n }\n\n .pro6pp-clear-button {\n background: none;\n border: none;\n width: 32px;\n height: 32px;\n cursor: pointer;\n color: #a3a3a3;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 50%;\n transition: color 0.2s, background-color 0.2s;\n touch-action: manipulation;\n }\n\n @media (hover: hover) {\n .pro6pp-clear-button:hover {\n color: #1f2937;\n background-color: #f3f4f6;\n }\n }\n\n .pro6pp-clear-button:active {\n background-color: #f3f4f6;\n }\n\n .pro6pp-dropdown {\n position: absolute;\n top: 100%;\n left: 0;\n right: 0;\n margin-top: 4px;\n background: #ffffff;\n border: 1px solid #e5e7eb;\n border-radius: 6px;\n box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);\n z-index: 9999;\n padding: 0;\n max-height: 280px;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n }\n\n @media (max-height: 500px) {\n .pro6pp-dropdown {\n max-height: 180px;\n }\n }\n\n .pro6pp-list {\n list-style: none;\n margin: 0;\n padding: 0;\n width: 100%;\n }\n\n .pro6pp-item {\n padding: 12px 14px;\n cursor: pointer;\n display: flex;\n align-items: baseline;\n font-size: 15px;\n line-height: 1.4;\n color: #374151;\n border-bottom: 1px solid #f3f4f6;\n transition: background-color 0.1s;\n flex-shrink: 0;\n }\n\n .pro6pp-item:last-child {\n border-bottom: none;\n }\n\n @media (hover: hover) {\n .pro6pp-item:hover, .pro6pp-item--active {\n background-color: #f9fafb;\n }\n }\n\n .pro6pp-item:active {\n background-color: #f3f4f6;\n }\n\n .pro6pp-item__label {\n font-weight: 400;\n flex-shrink: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n /* Place names are the primary result text. Keep them intact and let their\n secondary details give up space first. */\n .pro6pp-item--place .pro6pp-item__label {\n flex: 0 0 auto;\n max-width: 100%;\n overflow: visible;\n text-overflow: clip;\n white-space: normal;\n overflow-wrap: anywhere;\n }\n\n .pro6pp-item__label--match {\n /* A full weight step: intermediate values like 520 only render on\n variable fonts and leave the match invisible elsewhere. */\n font-weight: 700;\n }\n\n .pro6pp-item--fuzzy .pro6pp-item__label {\n /* Close matches read as suggestions, not statements: the label leans,\n while the shared characters keep the same bold emphasis. */\n font-style: italic;\n }\n\n .pro6pp-item__label--unmatched {\n font-weight: 400;\n color: #4b5563;\n }\n\n .pro6pp-item__subtitle {\n color: #6b7280;\n flex-shrink: 0;\n }\n\n .pro6pp-item--place .pro6pp-item__subtitle {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .pro6pp-item__chevron {\n color: #d1d5db;\n display: flex;\n align-items: center;\n align-self: center;\n margin-left: auto;\n padding-left: 8px;\n }\n\n .pro6pp-no-results {\n padding: 24px 16px;\n color: #6b7280;\n font-size: 15px;\n text-align: center;\n }\n\n .pro6pp-loader-item {\n padding: 10px 12px;\n color: #6b7280;\n font-size: 0.875rem;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n background-color: #f9fafb;\n border-top: 1px solid #f3f4f6;\n }\n\n .pro6pp-mini-spinner {\n width: 14px;\n height: 14px;\n border: 2px solid #e5e7eb;\n border-top-color: #6b7280;\n border-radius: 50%;\n animation: pro6pp-spin 0.6s linear infinite;\n }\n\n @media (max-width: 640px) {\n .pro6pp-input {\n font-size: 16px;\n padding: 10px 12px;\n }\n .pro6pp-item {\n padding: 10px 12px;\n font-size: 14px;\n }\n }\n\n @keyframes pro6pp-spin {\n to { transform: rotate(360deg); }\n }\n"; /** Countries currently supported by the Infer API for place suggestions. */ declare const INFER_PLACES_COUNTRIES: readonly ["NL", "BE", "DE", "FR"]; type InferPlacesCountryCode = (typeof INFER_PLACES_COUNTRIES)[number]; /** * Returns whether the Infer API supports place suggestions for a country. * Country codes are compared case-insensitively after trimming whitespace. */ declare function supportsInferPlaces(country: string): boolean; export { type AddressSuggestion, type AddressValue, type CountryCode, DEFAULT_STYLES, type Fetcher, type HighlightSegment, INFER_PLACES_COUNTRIES, INITIAL_STATE, type InferConfig, InferCore, type InferPlacesCountryCode, type InferResult, type InferState, type InferTypes, type LanguageCode, type PlaceSuggestion, type PlaceValue, type SortOrder, type Stage, formatLabelByInputOrder, getFuzzyHighlightSegments, getHighlightSegments, getOrderedItems, getPlaceAddress, supportsInferPlaces };