import type { ChatConfig } from "./types/config.js"; import type { Theme, UserIdentity } from "./types/messaging.js"; import type { ToolCallEventListener, ToolCallHandler } from "./types/tools.js"; declare global { interface Window { __YAK_INTERNAL_DEV__?: boolean; __YAK_LOGGING_ENABLED__?: boolean; } } export interface YakClientConfig { appId: string; /** * Override the origin the chat widget iframe loads from. Defaults to * `https://chat.yak.io`. Most integrators never set this — it exists for * non-production environments (e.g. a chat UI running on localhost). */ origin?: string; /** * Handler for tool calls from the chat widget. * The consuming platform decides how to execute (browser, server fetch, etc.) * * @example Browser-only execution * ```ts * onToolCall: async (name, args) => { * if (name === "ui.scrollTo") { * document.getElementById(args.id)?.scrollIntoView(); * return { success: true }; * } * throw new Error(`Unknown tool: ${name}`); * } * ``` * * @example Server delegation * ```ts * onToolCall: async (name, args) => { * const res = await fetch("/api/yak/tools", { * method: "POST", * body: JSON.stringify({ name, args }), * }); * const data = await res.json(); * if (!data.ok) throw new Error(data.error); * return data.result; * } * ``` */ onToolCall?: ToolCallHandler; theme?: Theme; chatConfig?: ChatConfig; onRedirect?: (path: string) => void; onClose?: () => void; onReady?: () => void; /** * Called after every tool call completes (success or failure). * Useful for page-level cache invalidation based on which tools were called. * * @example * ```ts * onToolCallComplete: (event) => { * if (event.ok && event.name.startsWith("order.")) { * queryClient.invalidateQueries({ queryKey: ["orders"] }); * } * } * ``` */ onToolCallComplete?: ToolCallEventListener; /** Chat configuration options */ options?: { /** Disable the restart session button in the header */ disableRestartButton?: boolean; /** * Stop sending any page context (URL, title, visible text) to the assistant. * The widget still works; it just won't be aware of the current page, so it * can't answer questions like "what is this page?". Use this when page content * must not leave the customer's site for privacy reasons. */ disablePageContent?: boolean; }; /** * Signed end-user identity. When supplied, the widget persists conversations * server-side keyed to this user and surfaces a history pane. The `hash` * must be HMAC-SHA256(apiSecret, id) computed on the integrator's backend — * never expose `apiSecret` to the browser. * * @example * ```ts * // Integrator backend (Node.js) * const hash = crypto * .createHmac("sha256", process.env.YAK_API_SECRET) * .update(currentUser.id) * .digest("hex"); * * // Browser * new YakClient({ * appId: "app_abc", * user: { id: currentUser.id, hash }, * }); * ``` */ user?: UserIdentity; } export declare class YakClient { private config; private iframeWindow; private isWidgetOpen; private readyTarget; private unexpectedOriginLogged; private lastUrl; private debouncedSendContext; private observer; constructor(config: YakClientConfig); updateConfig(newConfig: Partial): void; /** * Get the iframe origin URL (base URL for the chat widget). Recomputed on * each call so environment-dependent defaults resolve correctly. */ getIframeOrigin(): string; /** * Get the full iframe embed URL for the chatbot * * @example * ```ts * const client = new YakClient({ appId: "my-app" }); * const iframeSrc = client.getEmbedUrl(); * // Returns: "https://chat.yak.io/embed/v1/my-app" * ``` */ getEmbedUrl(): string; /** * Append theme color parameters to a URLSearchParams object */ private appendThemeColors; /** * Get the app ID */ getAppId(): string; /** * Get the current theme configuration */ getTheme(): Theme | undefined; /** * Send a prompt message to the chatbot iframe * Note: The iframe must be ready to receive messages (onReady callback must have fired) * * @example * ```ts * const client = new YakClient({ appId: "my-app", onReady: () => { * client.sendPrompt("Help me with my order"); * }}); * ``` */ sendPrompt(prompt: string): void; /** * Send a focus request to the chatbot iframe * This will focus the chat input field */ sendFocus(): void; /** * The live postMessage target. Prefers `readyTarget` — the window that * completed the `yak:ready` handshake — and falls back to the iframe's * `contentWindow`. The two are normally identical, but they're populated by * different events (the ready postMessage vs. the DOM `load` event) and can * briefly diverge (e.g. a remount under React StrictMode, where `isReady` * goes true before — or without — the load event repopulating * `iframeWindow`). Trusting `readyTarget` keeps `sendFocus`/`sendPrompt` * consistent with `isReady()` and avoids spurious "iframe not ready" warns. */ private getActiveTarget; /** * Check if the iframe is ready to receive messages */ isReady(): boolean; setIframeWindow(window: Window | null): void; setWidgetOpen(isOpen: boolean): void; mount(): void; unmount(): void; private startObserving; private stopObserving; private handlePopState; private handleMessage; private sendConfigToIframe; private sendPageContext; private handleToolCall; private sendToolResultToIframe; /** * Convert a value to a serializable form by stripping functions and other non-cloneable values. * Uses JSON.parse(JSON.stringify()) which handles most cases. */ private toSerializable; private extractErrorMessage; /** * Validates that a redirect path is safe (relative path or same-origin). * Blocks absolute URLs to external domains to prevent open redirect attacks. */ private isAllowedRedirect; } //# sourceMappingURL=client.d.ts.map