@navsi.ai/sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,66 @@
1
+ import React__default from 'react';
2
+ import { NavigationAdapter, ServerAction, FlatWidgetConfig, WidgetConfig } from '@navsi.ai/shared';
3
+
4
+ /**
5
+ * ChatbotProvider Component
6
+ *
7
+ * Main provider component that initializes the chatbot SDK.
8
+ * Manages WebSocket connection, message state, and provides context to children.
9
+ */
10
+
11
+ interface ChatbotProviderProps {
12
+ /** API key for authentication */
13
+ apiKey: string;
14
+ /** WebSocket server URL */
15
+ serverUrl: string;
16
+ /** Navigation adapter (defaults to memory adapter) */
17
+ navigationAdapter?: NavigationAdapter;
18
+ /** Server actions to register */
19
+ serverActions?: ServerAction[];
20
+ /** Widget config from API (flat shape). Fetched e.g. from GET /api/sdk/config. Takes precedence over options when merged. */
21
+ config?: FlatWidgetConfig | null;
22
+ /** Widget configuration (nested shape). Merged with config if both provided. */
23
+ options?: Partial<WidgetConfig>;
24
+ /** Optional list of available routes (included in page context for the AI) */
25
+ routes?: string[];
26
+ /** Enable debug mode */
27
+ debug?: boolean;
28
+ /** Auto-connect on mount */
29
+ autoConnect?: boolean;
30
+ /** Children components */
31
+ children: React__default.ReactNode;
32
+ }
33
+ declare function ChatbotProvider({ apiKey, serverUrl, navigationAdapter, serverActions, config: configFromApi, options, routes, debug, autoConnect, children, }: ChatbotProviderProps): React__default.ReactElement;
34
+
35
+ /**
36
+ * ChatbotWidget Component
37
+ *
38
+ * Floating chat widget UI component.
39
+ */
40
+
41
+ interface ChatbotWidgetProps {
42
+ /** Custom class name for the widget container */
43
+ className?: string;
44
+ /** Custom class name for the widget window */
45
+ windowClassName?: string;
46
+ /** Custom class name for the toggle button */
47
+ buttonClassName?: string;
48
+ }
49
+ /**
50
+ * Floating chat widget component. Open state is stored in context so the panel
51
+ * stays open across navigation (e.g. when using Action mode).
52
+ * Supports Ask and Action modes for text chat.
53
+ *
54
+ * All styling is controlled via CSS classes. Users have full control over appearance.
55
+ *
56
+ * @example
57
+ * ```tsx
58
+ * <ChatbotProvider apiKey="..." serverUrl="...">
59
+ * <App />
60
+ * <ChatbotWidget className="my-chatbot" />
61
+ * </ChatbotProvider>
62
+ * ```
63
+ */
64
+ declare function ChatbotWidget({ className, windowClassName, buttonClassName }: ChatbotWidgetProps): React__default.ReactElement;
65
+
66
+ export { ChatbotProvider, type ChatbotProviderProps, ChatbotWidget, type ChatbotWidgetProps };
@@ -0,0 +1,4 @@
1
+ export { ChatbotProvider, ChatbotWidget } from '../chunk-EHZXIZIP.js';
2
+ import '../chunk-6FUUG5WB.js';
3
+ //# sourceMappingURL=index.js.map
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
@@ -0,0 +1,104 @@
1
+ import { WidgetConfig, Message, ChatMode, ServerAction, ConnectionState, ExecutionState } from '@navsi.ai/shared';
2
+
3
+ /**
4
+ * useChatbot Hook
5
+ *
6
+ * Main hook for accessing chatbot functionality.
7
+ * Provides simplified API for common operations.
8
+ */
9
+
10
+ interface UseChatbotReturn {
11
+ /** Widget styling/copy from admin (or options). Use in ChatbotWidget. */
12
+ widgetConfig: Partial<WidgetConfig> | null;
13
+ /** Effective voice/response language (user-selected or config). Use for STT/TTS and locale. */
14
+ voiceLanguage: string | undefined;
15
+ setVoiceLanguage: (lang: string | undefined) => void;
16
+ messages: Message[];
17
+ sendMessage: (content: string) => void;
18
+ clearMessages: () => void;
19
+ mode: ChatMode;
20
+ setMode: (mode: ChatMode) => void;
21
+ isWidgetOpen: boolean;
22
+ setWidgetOpen: (open: boolean) => void;
23
+ isConnected: boolean;
24
+ connect: () => void;
25
+ disconnect: () => void;
26
+ executeAction: (actionId: string, params?: Record<string, unknown>) => Promise<unknown>;
27
+ registerServerAction: (action: ServerAction) => void;
28
+ stopExecution: () => void;
29
+ isExecuting: boolean;
30
+ error: Error | null;
31
+ clearError: () => void;
32
+ }
33
+ /**
34
+ * Main hook for chatbot functionality
35
+ *
36
+ * @example
37
+ * ```tsx
38
+ * function MyComponent() {
39
+ * const {
40
+ * messages,
41
+ * sendMessage,
42
+ * isConnected,
43
+ * mode,
44
+ * setMode,
45
+ * } = useChatbot();
46
+ *
47
+ * return (
48
+ * <div>
49
+ * <button onClick={() => sendMessage('Hello!')}>
50
+ * Send
51
+ * </button>
52
+ * <button onClick={() => startVoice()}>
53
+ * Start Voice
54
+ * </button>
55
+ * </div>
56
+ * );
57
+ * }
58
+ * ```
59
+ */
60
+ declare function useChatbot(): UseChatbotReturn;
61
+
62
+ /**
63
+ * useWebSocket Hook
64
+ *
65
+ * Hook for managing WebSocket connection state.
66
+ */
67
+
68
+ interface UseWebSocketReturn {
69
+ connectionState: ConnectionState;
70
+ isConnected: boolean;
71
+ isConnecting: boolean;
72
+ isReconnecting: boolean;
73
+ connect: () => void;
74
+ disconnect: () => void;
75
+ }
76
+ /**
77
+ * Hook for WebSocket connection management
78
+ */
79
+ declare function useWebSocket(): UseWebSocketReturn;
80
+
81
+ /**
82
+ * useActionExecution Hook
83
+ *
84
+ * Hook for monitoring action execution state.
85
+ */
86
+
87
+ interface UseActionExecutionReturn {
88
+ isExecuting: boolean;
89
+ executionState: ExecutionState | null;
90
+ currentCommand: ExecutionState['currentCommand'] | undefined;
91
+ progress: {
92
+ current: number;
93
+ total: number;
94
+ percentage: number;
95
+ /** Optional human-readable description of the current step */
96
+ description?: string;
97
+ } | null;
98
+ }
99
+ /**
100
+ * Hook for monitoring action execution
101
+ */
102
+ declare function useActionExecution(): UseActionExecutionReturn;
103
+
104
+ export { type UseActionExecutionReturn, type UseChatbotReturn, type UseWebSocketReturn, useActionExecution, useChatbot, useWebSocket };
@@ -0,0 +1,4 @@
1
+ import '../chunk-427NHGTX.js';
2
+ export { useActionExecution, useChatbot, useWebSocket } from '../chunk-6FUUG5WB.js';
3
+ //# sourceMappingURL=index.js.map
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
@@ -0,0 +1,286 @@
1
+ export { ChatbotProvider, ChatbotProviderProps, ChatbotWidget, ChatbotWidgetProps } from './components/index.js';
2
+ export { UseActionExecutionReturn, UseChatbotReturn, UseWebSocketReturn, useActionExecution, useChatbot, useWebSocket } from './hooks/index.js';
3
+ import * as React from 'react';
4
+ import { WidgetConfig, Message, ChatMode, ConnectionState as ConnectionState$1, ExecutionState, ServerAction, ClientMessage, WebSocketEvents, NavigationAdapter, PageContext } from '@navsi.ai/shared';
5
+ export { ActionDefinition, ChatMode, Command, ExecutionState, Message, NavigationAdapter, PageContext, SDKConfig, ServerAction, WidgetConfig, WidgetTheme } from '@navsi.ai/shared';
6
+
7
+ interface ChatbotContextValue {
8
+ /** Widget styling and copy (from admin / API). Applied by ChatbotWidget. */
9
+ widgetConfig: Partial<WidgetConfig> | null;
10
+ messages: Message[];
11
+ mode: ChatMode;
12
+ connectionState: ConnectionState$1;
13
+ isExecuting: boolean;
14
+ executionState: ExecutionState | null;
15
+ error: Error | null;
16
+ isWidgetOpen: boolean;
17
+ setWidgetOpen: (open: boolean) => void;
18
+ /** User-selected voice/response language (e.g. 'en-US', 'hi-IN'). Overrides widgetConfig.voiceLanguage when set. */
19
+ voiceLanguage: string | undefined;
20
+ setVoiceLanguage: (lang: string | undefined) => void;
21
+ sendMessage: (content: string) => void;
22
+ setMode: (mode: ChatMode) => void;
23
+ executeAction: (actionId: string, params?: Record<string, unknown>) => Promise<unknown>;
24
+ registerServerAction: (action: ServerAction) => void;
25
+ clearMessages: () => void;
26
+ clearError: () => void;
27
+ stopExecution: () => void;
28
+ connect: () => void;
29
+ disconnect: () => void;
30
+ isConnected: boolean;
31
+ }
32
+ declare const ChatbotContext: React.Context<ChatbotContextValue | null>;
33
+ /**
34
+ * Hook to access chatbot context
35
+ * Must be used within ChatbotProvider
36
+ */
37
+ declare function useChatbotContext(): ChatbotContextValue;
38
+
39
+ /**
40
+ * WebSocket Client for AI Chatbot SDK
41
+ *
42
+ * Features:
43
+ * - Auto-reconnection with exponential backoff
44
+ * - Message queue for offline scenarios
45
+ * - Heartbeat mechanism (ping/pong every 30s)
46
+ * - Token refresh before expiry
47
+ * - Connection state management
48
+ */
49
+
50
+ type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'reconnecting' | 'failed';
51
+ interface WebSocketClientConfig {
52
+ /** WebSocket server URL */
53
+ serverUrl: string;
54
+ /** API key for authentication */
55
+ apiKey: string;
56
+ /** Enable auto-reconnection */
57
+ autoReconnect?: boolean;
58
+ /** Maximum reconnection attempts */
59
+ maxReconnectAttempts?: number;
60
+ /** Initial reconnection delay in ms */
61
+ reconnectDelay?: number;
62
+ /** Heartbeat interval in ms */
63
+ heartbeatInterval?: number;
64
+ /** Enable debug logging */
65
+ debug?: boolean;
66
+ }
67
+ type EventCallback<T> = (data: T) => void;
68
+ declare class WebSocketClient {
69
+ private ws;
70
+ private config;
71
+ private state;
72
+ private logger;
73
+ private reconnectAttempts;
74
+ private reconnectTimeout;
75
+ private heartbeatInterval;
76
+ private lastPongTime;
77
+ private messageQueue;
78
+ private sessionId;
79
+ private refreshToken;
80
+ private tokenExpiresAt;
81
+ private tokenRefreshTimeout;
82
+ private listeners;
83
+ constructor(config: WebSocketClientConfig);
84
+ /**
85
+ * Connect to the WebSocket server
86
+ */
87
+ connect(): void;
88
+ /**
89
+ * Disconnect from the server
90
+ */
91
+ disconnect(): void;
92
+ /**
93
+ * Send a message to the server
94
+ */
95
+ send(message: ClientMessage): void;
96
+ /**
97
+ * Get current connection state
98
+ */
99
+ getState(): ConnectionState;
100
+ /**
101
+ * Get session ID
102
+ */
103
+ getSessionId(): string | null;
104
+ /**
105
+ * Check if connected
106
+ */
107
+ isConnected(): boolean;
108
+ /**
109
+ * Subscribe to an event
110
+ */
111
+ on<K extends keyof WebSocketEvents>(event: K, callback: EventCallback<WebSocketEvents[K]>): () => void;
112
+ /**
113
+ * Emit an event to all listeners
114
+ */
115
+ private emit;
116
+ private createWebSocket;
117
+ private handleOpen;
118
+ private handleMessage;
119
+ private handleClose;
120
+ private handleError;
121
+ private handleConnected;
122
+ private handleAuthSuccess;
123
+ private handleAuthError;
124
+ private scheduleTokenRefresh;
125
+ private refreshAccessToken;
126
+ private handleTokenRefreshed;
127
+ private startHeartbeat;
128
+ private stopHeartbeat;
129
+ private handlePong;
130
+ private scheduleReconnect;
131
+ private handleConnectionFailure;
132
+ private flushMessageQueue;
133
+ private setState;
134
+ private cleanup;
135
+ private log;
136
+ }
137
+ declare function createWebSocketClient(config: WebSocketClientConfig): WebSocketClient;
138
+
139
+ /**
140
+ * Navigation Adapter Interface
141
+ *
142
+ * Abstract interface for routing library integration.
143
+ * Companies can implement this for any routing solution:
144
+ * - React Router v6
145
+ * - Next.js Router
146
+ * - Reach Router
147
+ * - Custom routing
148
+ */
149
+
150
+ /**
151
+ * Create a simple in-memory navigation adapter
152
+ * Useful for testing or non-SPA applications
153
+ */
154
+ declare function createMemoryAdapter(initialPath?: string): NavigationAdapter;
155
+
156
+ /**
157
+ * React Router v6 Adapter
158
+ *
159
+ * Provides navigation adapter implementation for React Router v6.
160
+ * This is a hook that creates an adapter from React Router hooks.
161
+ */
162
+
163
+ /**
164
+ * Type for React Router's navigate function
165
+ */
166
+ type NavigateFunction = (to: string) => void;
167
+ /**
168
+ * Type for React Router's location object
169
+ */
170
+ interface RouterLocation {
171
+ pathname: string;
172
+ }
173
+ /**
174
+ * Creates a React Router v6 navigation adapter
175
+ *
176
+ * @example
177
+ * ```tsx
178
+ * import { useNavigate, useLocation } from 'react-router-dom';
179
+ *
180
+ * function App() {
181
+ * const navigate = useNavigate();
182
+ * const location = useLocation();
183
+ * const adapter = useReactRouterAdapter(navigate, location);
184
+ *
185
+ * return (
186
+ * <ChatbotProvider navigationAdapter={adapter}>
187
+ * ...
188
+ * </ChatbotProvider>
189
+ * );
190
+ * }
191
+ * ```
192
+ */
193
+ declare function useReactRouterAdapter(navigate: NavigateFunction, location: RouterLocation): NavigationAdapter;
194
+ /**
195
+ * Create a React Router adapter from outside React context
196
+ * Useful when you need to create the adapter before rendering
197
+ *
198
+ * @param getNavigate Function that returns the navigate function
199
+ * @param getLocation Function that returns the current location
200
+ */
201
+ declare function createReactRouterAdapter(getNavigate: () => NavigateFunction, getLocation: () => RouterLocation, subscribeToLocation: (callback: (location: RouterLocation) => void) => () => void): NavigationAdapter;
202
+
203
+ /**
204
+ * Navigation Controller
205
+ *
206
+ * Coordinates navigation and DOM stability detection for cross-route actions.
207
+ */
208
+
209
+ interface NavigationControllerConfig {
210
+ /** DOM stability quiet period in ms */
211
+ domStabilityDelay?: number;
212
+ /** Maximum wait time for DOM stability */
213
+ domStabilityTimeout?: number;
214
+ /** Enable debug logging */
215
+ debug?: boolean;
216
+ /** Called when DOM has become stable (e.g. to re-scan and update action registry) */
217
+ onDOMStable?: () => void;
218
+ }
219
+ declare class NavigationController {
220
+ private adapter;
221
+ private config;
222
+ private scanCallback;
223
+ private logger;
224
+ constructor(adapter: NavigationAdapter, config?: NavigationControllerConfig);
225
+ /**
226
+ * Set the page scanner callback
227
+ */
228
+ setScanner(scanner: () => Promise<PageContext>): void;
229
+ /**
230
+ * Get current route
231
+ */
232
+ getCurrentRoute(): string;
233
+ /**
234
+ * Navigate to a route and wait for it to load
235
+ */
236
+ navigateTo(route: string, timeout?: number): Promise<boolean>;
237
+ /**
238
+ * Execute navigation and rescan page context
239
+ */
240
+ executeNavigation(route: string): Promise<PageContext | null>;
241
+ /**
242
+ * Wait for DOM to become stable after navigation
243
+ * Uses MutationObserver to detect when DOM stops changing
244
+ */
245
+ waitForDOMStable(): Promise<void>;
246
+ /**
247
+ * Subscribe to route changes
248
+ */
249
+ onRouteChange(callback: (route: string) => void): () => void;
250
+ private log;
251
+ }
252
+ /**
253
+ * Create a navigation controller
254
+ */
255
+ declare function createNavigationController(adapter: NavigationAdapter, config?: NavigationControllerConfig): NavigationController;
256
+
257
+ /** Next.js App Router: router from useRouter(), pathname from usePathname() */
258
+ type NextRouterLike = {
259
+ push: (href: string) => void;
260
+ };
261
+ /**
262
+ * Creates a Next.js App Router navigation adapter.
263
+ * Must be used in a Client Component. Requires next to be installed.
264
+ *
265
+ * @example
266
+ * ```tsx
267
+ * 'use client';
268
+ * import { useRouter, usePathname } from 'next/navigation';
269
+ * import { ChatbotProvider, ChatbotWidget, useNextRouterAdapter } from '@navsi/sdk';
270
+ *
271
+ * function ChatbotWrapper({ children }: { children: React.ReactNode }) {
272
+ * const router = useRouter();
273
+ * const pathname = usePathname();
274
+ * const adapter = useNextRouterAdapter(router, pathname);
275
+ * return (
276
+ * <ChatbotProvider apiKey="..." serverUrl="..." navigationAdapter={adapter}>
277
+ * {children}
278
+ * <ChatbotWidget />
279
+ * </ChatbotProvider>
280
+ * );
281
+ * }
282
+ * ```
283
+ */
284
+ declare function useNextRouterAdapter(router: NextRouterLike, pathname: string): NavigationAdapter;
285
+
286
+ export { ChatbotContext, type ChatbotContextValue, type ConnectionState, type NavigateFunction, NavigationController, type NavigationControllerConfig, type NextRouterLike, type RouterLocation, WebSocketClient, type WebSocketClientConfig, createMemoryAdapter, createNavigationController, createReactRouterAdapter, createWebSocketClient, useChatbotContext, useNextRouterAdapter, useReactRouterAdapter };
package/dist/index.js ADDED
@@ -0,0 +1,182 @@
1
+ import { DEFAULT_ROUTE_TIMEOUT } from './chunk-EHZXIZIP.js';
2
+ export { ChatbotProvider, ChatbotWidget, NavigationController, WebSocketClient, createMemoryAdapter, createNavigationController, createWebSocketClient } from './chunk-EHZXIZIP.js';
3
+ import './chunk-427NHGTX.js';
4
+ export { ChatbotContext, useActionExecution, useChatbot, useChatbotContext, useWebSocket } from './chunk-6FUUG5WB.js';
5
+ import { useRef, useEffect, useCallback } from 'react';
6
+
7
+ function useReactRouterAdapter(navigate, location) {
8
+ const listenersRef = useRef(/* @__PURE__ */ new Set());
9
+ const currentPathRef = useRef(location.pathname);
10
+ useEffect(() => {
11
+ if (location.pathname !== currentPathRef.current) {
12
+ currentPathRef.current = location.pathname;
13
+ listenersRef.current.forEach((callback) => {
14
+ callback(location.pathname);
15
+ });
16
+ }
17
+ }, [location.pathname]);
18
+ const navigateTo = useCallback((path) => {
19
+ navigate(path);
20
+ }, [navigate]);
21
+ const getCurrentPath = useCallback(() => {
22
+ return currentPathRef.current;
23
+ }, []);
24
+ const onRouteChange = useCallback((callback) => {
25
+ listenersRef.current.add(callback);
26
+ return () => {
27
+ listenersRef.current.delete(callback);
28
+ };
29
+ }, []);
30
+ const waitForRoute = useCallback(async (path, timeout = DEFAULT_ROUTE_TIMEOUT) => {
31
+ return new Promise((resolve) => {
32
+ if (currentPathRef.current === path) {
33
+ resolve(true);
34
+ return;
35
+ }
36
+ let resolved = false;
37
+ const unsubscribe = onRouteChange((newPath) => {
38
+ if (newPath === path && !resolved) {
39
+ resolved = true;
40
+ unsubscribe();
41
+ resolve(true);
42
+ }
43
+ });
44
+ setTimeout(() => {
45
+ if (!resolved) {
46
+ resolved = true;
47
+ unsubscribe();
48
+ resolve(currentPathRef.current === path);
49
+ }
50
+ }, timeout);
51
+ });
52
+ }, [onRouteChange]);
53
+ const adapterRef = useRef(null);
54
+ if (!adapterRef.current) {
55
+ adapterRef.current = {
56
+ navigate: navigateTo,
57
+ getCurrentPath,
58
+ onRouteChange,
59
+ waitForRoute
60
+ };
61
+ } else {
62
+ adapterRef.current.navigate = navigateTo;
63
+ adapterRef.current.getCurrentPath = getCurrentPath;
64
+ adapterRef.current.onRouteChange = onRouteChange;
65
+ adapterRef.current.waitForRoute = waitForRoute;
66
+ }
67
+ return adapterRef.current;
68
+ }
69
+ function createReactRouterAdapter(getNavigate, getLocation, subscribeToLocation) {
70
+ const listeners = /* @__PURE__ */ new Set();
71
+ subscribeToLocation((location) => {
72
+ listeners.forEach((callback) => callback(location.pathname));
73
+ });
74
+ return {
75
+ navigate(path) {
76
+ getNavigate()(path);
77
+ },
78
+ getCurrentPath() {
79
+ return getLocation().pathname;
80
+ },
81
+ onRouteChange(callback) {
82
+ listeners.add(callback);
83
+ return () => listeners.delete(callback);
84
+ },
85
+ async waitForRoute(path, timeout = DEFAULT_ROUTE_TIMEOUT) {
86
+ return new Promise((resolve) => {
87
+ const currentPath = getLocation().pathname;
88
+ if (currentPath === path) {
89
+ resolve(true);
90
+ return;
91
+ }
92
+ let resolved = false;
93
+ const unsubscribe = this.onRouteChange((newPath) => {
94
+ if (newPath === path && !resolved) {
95
+ resolved = true;
96
+ unsubscribe();
97
+ resolve(true);
98
+ }
99
+ });
100
+ setTimeout(() => {
101
+ if (!resolved) {
102
+ resolved = true;
103
+ unsubscribe();
104
+ resolve(getLocation().pathname === path);
105
+ }
106
+ }, timeout);
107
+ });
108
+ }
109
+ };
110
+ }
111
+ function useNextRouterAdapter(router, pathname) {
112
+ const listenersRef = useRef(/* @__PURE__ */ new Set());
113
+ const currentPathRef = useRef(pathname);
114
+ useEffect(() => {
115
+ if (pathname !== currentPathRef.current) {
116
+ currentPathRef.current = pathname;
117
+ listenersRef.current.forEach((callback) => {
118
+ callback(pathname);
119
+ });
120
+ }
121
+ }, [pathname]);
122
+ const navigateTo = useCallback(
123
+ (path) => {
124
+ router.push(path);
125
+ },
126
+ [router]
127
+ );
128
+ const getCurrentPath = useCallback(() => {
129
+ return currentPathRef.current;
130
+ }, []);
131
+ const onRouteChange = useCallback((callback) => {
132
+ listenersRef.current.add(callback);
133
+ return () => {
134
+ listenersRef.current.delete(callback);
135
+ };
136
+ }, []);
137
+ const waitForRoute = useCallback(
138
+ async (path, timeout = DEFAULT_ROUTE_TIMEOUT) => {
139
+ return new Promise((resolve) => {
140
+ if (currentPathRef.current === path) {
141
+ resolve(true);
142
+ return;
143
+ }
144
+ let resolved = false;
145
+ const unsubscribe = onRouteChange((newPath) => {
146
+ if (newPath === path && !resolved) {
147
+ resolved = true;
148
+ unsubscribe();
149
+ resolve(true);
150
+ }
151
+ });
152
+ setTimeout(() => {
153
+ if (!resolved) {
154
+ resolved = true;
155
+ unsubscribe();
156
+ resolve(currentPathRef.current === path);
157
+ }
158
+ }, timeout);
159
+ });
160
+ },
161
+ [onRouteChange]
162
+ );
163
+ const adapterRef = useRef(null);
164
+ if (!adapterRef.current) {
165
+ adapterRef.current = {
166
+ navigate: navigateTo,
167
+ getCurrentPath,
168
+ onRouteChange,
169
+ waitForRoute
170
+ };
171
+ } else {
172
+ adapterRef.current.navigate = navigateTo;
173
+ adapterRef.current.getCurrentPath = getCurrentPath;
174
+ adapterRef.current.onRouteChange = onRouteChange;
175
+ adapterRef.current.waitForRoute = waitForRoute;
176
+ }
177
+ return adapterRef.current;
178
+ }
179
+
180
+ export { createReactRouterAdapter, useNextRouterAdapter, useReactRouterAdapter };
181
+ //# sourceMappingURL=index.js.map
182
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/navigation/react-router-adapter.ts","../src/core/navigation/nextjs-adapter.ts"],"names":["useRef","useEffect","useCallback"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * React Router v6 Adapter\n * \n * Provides navigation adapter implementation for React Router v6.\n * This is a hook that creates an adapter from React Router hooks.\n */\n\nimport { useCallback, useEffect, useRef } from 'react';\nimport type { NavigationAdapter } from '@navsi.ai/shared';\nimport { DEFAULT_ROUTE_TIMEOUT } from './adapter.js';\n\n/**\n * Type for React Router's navigate function\n */\nexport type NavigateFunction = (to: string) => void;\n\n/**\n * Type for React Router's location object\n */\nexport interface RouterLocation {\n pathname: string;\n}\n\n/**\n * Creates a React Router v6 navigation adapter\n * \n * @example\n * ```tsx\n * import { useNavigate, useLocation } from 'react-router-dom';\n * \n * function App() {\n * const navigate = useNavigate();\n * const location = useLocation();\n * const adapter = useReactRouterAdapter(navigate, location);\n * \n * return (\n * <ChatbotProvider navigationAdapter={adapter}>\n * ...\n * </ChatbotProvider>\n * );\n * }\n * ```\n */\nexport function useReactRouterAdapter(\n navigate: NavigateFunction,\n location: RouterLocation\n): NavigationAdapter {\n const listenersRef = useRef<Set<(path: string) => void>>(new Set());\n const currentPathRef = useRef(location.pathname);\n\n // Update current path and notify listeners when location changes\n useEffect(() => {\n if (location.pathname !== currentPathRef.current) {\n currentPathRef.current = location.pathname;\n listenersRef.current.forEach((callback) => {\n callback(location.pathname);\n });\n }\n }, [location.pathname]);\n\n const navigateTo = useCallback((path: string) => {\n navigate(path);\n }, [navigate]);\n\n const getCurrentPath = useCallback(() => {\n return currentPathRef.current;\n }, []);\n\n const onRouteChange = useCallback((callback: (path: string) => void) => {\n listenersRef.current.add(callback);\n return () => {\n listenersRef.current.delete(callback);\n };\n }, []);\n\n const waitForRoute = useCallback(async (path: string, timeout = DEFAULT_ROUTE_TIMEOUT): Promise<boolean> => {\n return new Promise((resolve) => {\n if (currentPathRef.current === path) {\n resolve(true);\n return;\n }\n\n let resolved = false;\n\n const unsubscribe = onRouteChange((newPath) => {\n if (newPath === path && !resolved) {\n resolved = true;\n unsubscribe();\n resolve(true);\n }\n });\n\n setTimeout(() => {\n if (!resolved) {\n resolved = true;\n unsubscribe();\n resolve(currentPathRef.current === path);\n }\n }, timeout);\n });\n }, [onRouteChange]);\n\n // Return a stable reference to the adapter\n const adapterRef = useRef<NavigationAdapter | null>(null);\n\n if (!adapterRef.current) {\n adapterRef.current = {\n navigate: navigateTo,\n getCurrentPath,\n onRouteChange,\n waitForRoute,\n };\n } else {\n // Update functions to use latest refs\n adapterRef.current.navigate = navigateTo;\n adapterRef.current.getCurrentPath = getCurrentPath;\n adapterRef.current.onRouteChange = onRouteChange;\n adapterRef.current.waitForRoute = waitForRoute;\n }\n\n return adapterRef.current;\n}\n\n/**\n * Create a React Router adapter from outside React context\n * Useful when you need to create the adapter before rendering\n * \n * @param getNavigate Function that returns the navigate function\n * @param getLocation Function that returns the current location\n */\nexport function createReactRouterAdapter(\n getNavigate: () => NavigateFunction,\n getLocation: () => RouterLocation,\n subscribeToLocation: (callback: (location: RouterLocation) => void) => () => void\n): NavigationAdapter {\n const listeners = new Set<(path: string) => void>();\n\n // Subscribe to location changes\n subscribeToLocation((location) => {\n listeners.forEach((callback) => callback(location.pathname));\n });\n\n return {\n navigate(path: string): void {\n getNavigate()(path);\n },\n\n getCurrentPath(): string {\n return getLocation().pathname;\n },\n\n onRouteChange(callback: (path: string) => void): () => void {\n listeners.add(callback);\n return () => listeners.delete(callback);\n },\n\n async waitForRoute(path: string, timeout = DEFAULT_ROUTE_TIMEOUT): Promise<boolean> {\n return new Promise((resolve) => {\n const currentPath = getLocation().pathname;\n\n if (currentPath === path) {\n resolve(true);\n return;\n }\n\n let resolved = false;\n\n const unsubscribe = this.onRouteChange((newPath) => {\n if (newPath === path && !resolved) {\n resolved = true;\n unsubscribe();\n resolve(true);\n }\n });\n\n setTimeout(() => {\n if (!resolved) {\n resolved = true;\n unsubscribe();\n resolve(getLocation().pathname === path);\n }\n }, timeout);\n });\n },\n };\n}\n","'use client';\n/**\n * Next.js App Router Adapter\n *\n * Provides navigation adapter implementation for Next.js App Router.\n * Uses next/navigation: useRouter(), usePathname().\n */\n\nimport { useCallback, useEffect, useRef } from 'react';\nimport type { NavigationAdapter } from '@navsi.ai/shared';\nimport { DEFAULT_ROUTE_TIMEOUT } from './adapter.js';\n\n/** Next.js App Router: router from useRouter(), pathname from usePathname() */\nexport type NextRouterLike = { push: (href: string) => void };\n\n/**\n * Creates a Next.js App Router navigation adapter.\n * Must be used in a Client Component. Requires next to be installed.\n *\n * @example\n * ```tsx\n * 'use client';\n * import { useRouter, usePathname } from 'next/navigation';\n * import { ChatbotProvider, ChatbotWidget, useNextRouterAdapter } from '@navsi/sdk';\n *\n * function ChatbotWrapper({ children }: { children: React.ReactNode }) {\n * const router = useRouter();\n * const pathname = usePathname();\n * const adapter = useNextRouterAdapter(router, pathname);\n * return (\n * <ChatbotProvider apiKey=\"...\" serverUrl=\"...\" navigationAdapter={adapter}>\n * {children}\n * <ChatbotWidget />\n * </ChatbotProvider>\n * );\n * }\n * ```\n */\nexport function useNextRouterAdapter(\n router: NextRouterLike,\n pathname: string\n): NavigationAdapter {\n const listenersRef = useRef<Set<(path: string) => void>>(new Set());\n const currentPathRef = useRef(pathname);\n\n useEffect(() => {\n if (pathname !== currentPathRef.current) {\n currentPathRef.current = pathname;\n listenersRef.current.forEach((callback) => {\n callback(pathname);\n });\n }\n }, [pathname]);\n\n const navigateTo = useCallback(\n (path: string) => {\n router.push(path);\n },\n [router]\n );\n\n const getCurrentPath = useCallback(() => {\n return currentPathRef.current;\n }, []);\n\n const onRouteChange = useCallback((callback: (path: string) => void) => {\n listenersRef.current.add(callback);\n return () => {\n listenersRef.current.delete(callback);\n };\n }, []);\n\n const waitForRoute = useCallback(\n async (path: string, timeout = DEFAULT_ROUTE_TIMEOUT): Promise<boolean> => {\n return new Promise((resolve) => {\n if (currentPathRef.current === path) {\n resolve(true);\n return;\n }\n\n let resolved = false;\n\n const unsubscribe = onRouteChange((newPath) => {\n if (newPath === path && !resolved) {\n resolved = true;\n unsubscribe();\n resolve(true);\n }\n });\n\n setTimeout(() => {\n if (!resolved) {\n resolved = true;\n unsubscribe();\n resolve(currentPathRef.current === path);\n }\n }, timeout);\n });\n },\n [onRouteChange]\n );\n\n const adapterRef = useRef<NavigationAdapter | null>(null);\n\n if (!adapterRef.current) {\n adapterRef.current = {\n navigate: navigateTo,\n getCurrentPath,\n onRouteChange,\n waitForRoute,\n };\n } else {\n adapterRef.current.navigate = navigateTo;\n adapterRef.current.getCurrentPath = getCurrentPath;\n adapterRef.current.onRouteChange = onRouteChange;\n adapterRef.current.waitForRoute = waitForRoute;\n }\n\n return adapterRef.current;\n}\n"]}