import { ComponentPluginFactory } from '@teleporthq/teleport-types'; interface AIChatSessionPersistencePluginConfig { /** Output path of the generator using this plugin: `['components']` / `['pages']`. */ basePath: string[]; } /** * Keeps the visitor's conversation alive across client-side navigation. * * The chat is a component instance placed on each page, so following a link * unmounts it and React discards the transcript — the visitor loses their * conversation for clicking through to a product. This mirrors the two states * that make up a conversation into a module-level store that outlives the * unmount (see `session-store.ts` for why it is a `globalThis` bag and not * storage). * * Emitted shape: * * const [chatMessages, setChatMessages] = useState([...]) * const [chatConversationId, setChatConversationId] = useState('') * const __aiChatSessionRestored = useRef(false) * useEffect(() => { * const restoredChatMessages = restoreAIChatMessages(null) * if (restoredChatMessages) { setChatMessages(restoredChatMessages) } * const restoredChatConversationId = restoreAIChatConversationId(null) * if (restoredChatConversationId) { * setChatConversationId(restoredChatConversationId) * } * }, []) * useEffect(() => { * if (!__aiChatSessionRestored.current) { * __aiChatSessionRestored.current = true * return * } * persistAIChatSession(chatMessages, chatConversationId) * }, [chatMessages, chatConversationId]) * * ## ⛔ The restore MUST happen in an effect, never in the `useState` initializer * * The initializer runs DURING RENDER, on the server as well as the client. The * server has no session, so it renders the welcome bubble alone; the client * reads `sessionStorage` and renders the whole transcript. React compares the * two and throws "Hydration failed because the initial UI does not match what * was rendered on the server" — on every page load carrying a conversation, * which is exactly what following a product link out of the chat does. * * This shipped as an initializer while the store was a `globalThis` bag, where * a document load genuinely did start empty. Adding `sessionStorage` made the * store survive document loads and silently invalidated that reasoning. The * rule that survives both designs: an effect runs after the first paint, so the * first client render is the literal the server also rendered, and the restored * transcript arrives in a second render React performs itself. * * ⛔ The persist effect SKIPS its first run. It fires on mount before the * restore's `setState` has committed, so it would see the empty initial state * and `persistAIChatSession` would clear the very session being restored. * * ⛔ Runs BEFORE `createAIChatLocalizedWelcomePlugin`, so that plugin can wrap * BOTH values it owns — the `useState` initializer and the restored transcript * (`localizeAIChatMessages(restoreAIChatMessages(null), __aiChatLocale)`, which * is null-safe) — so a transcript restored on a page in another language still * gets its welcome bubble re-localized. * * The mirror effect covers workflow-driven updates too — `state-update-local- * state` calls the component's real setter, so clearing the conversation from * the chat header re-renders and empties the store through the same path. */ export declare const createAIChatSessionPersistencePlugin: ComponentPluginFactory; export {}; //# sourceMappingURL=session-persistence-plugin.d.ts.map