/** * Outbound Feature (banner surface, v1) * * Ships in the lazy `outbound.js` bundle, not in `array.js` — `OutboundWrapper` * fetches it on idle and only when the host page enabled outbound. Work stays * off first paint: the `/decide` fetch runs on idle, and the DOM is only * touched when there is a banner to show. Re-evaluates on SPA route change. * * Pipeline: fetch `/api/outbound/widget/decide` → drop locally-dismissed → * render the highest-priority banner → emit `impression`; CTA clicks emit * `cta_click` + `engagement` in one request (+ perform the action); the × emits * `dismiss` and suppresses the message locally. * * SPA navigation is bounded: at most one decide is pending (earlier idle tasks * are cancelled) and at most one is in flight (earlier fetches are aborted). A * re-decide that returns the same message leaves the mounted banner untouched, * so a sitewide banner doesn't re-shift the page or double-count impressions. * * @see docs/modules/outbound-messaging.md */ import type { VTilt } from "../../vtilt"; import type { VTiltConfig } from "../../types"; import type { Feature, FeatureConfig } from "../../feature"; export type OutboundFeatureConfig = FeatureConfig; export declare class OutboundFeature implements Feature { readonly name = "Outbound"; private _instance; private _config; private _isStarted; private _current; private _lastPathname; private _popstateUnsub; private _pageviewUnsub; /** Monotonic decide token — the latest in-flight decide wins (drops stale). */ private _decideSeq; /** Message id currently mounted, so a re-decide can keep it untouched. */ private _currentId; /** Handles for a scheduled (not yet run) decide — cancelled on nav / stop. */ private _idleHandle; private _timeoutHandle; /** Aborts the in-flight decide once a newer navigation supersedes it. */ private _abort; constructor(instance: VTilt, config?: OutboundFeatureConfig); static extractConfig(config: VTiltConfig): OutboundFeatureConfig; get isEnabled(): boolean; get isStarted(): boolean; startIfEnabled(): void; stop(): void; onConfigUpdate(config: VTiltConfig): void; private _scheduleDecide; /** Cancel a decide that was scheduled but hasn't started running yet. */ private _cancelScheduledDecide; /** Abort the in-flight decide request so it never reaches the server. */ private _abortInFlight; private _runDecide; private _fetchDecide; private _renderBanner; private _teardownBanner; private _onCta; private _onDismiss; /** * Send one or more outcome events in a single request. Pass an array when a * single interaction produces several events (a CTA click is also engagement) * so it costs one `POST`, not one per event. */ private _emit; private _readDismissed; private _isDismissed; private _addDismissed; private _watchRouteChanges; private _onRouteChange; }