@riktajs/react 0.10.3 → 0.11.4

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.
package/README.md CHANGED
@@ -172,13 +172,17 @@ function Breadcrumbs() {
172
172
 
173
173
  ### `useSsrData()`
174
174
 
175
- Access SSR data passed from server via `window.__SSR_DATA__`.
175
+ Access SSR data passed from server via `window.__SSR_DATA__`. The data structure includes:
176
+ - `data`: The actual page data from the controller
177
+ - `url`: The current URL
178
+ - `title`: Page title (from `@Ssr` decorator)
179
+ - `description`: Page description (from `@Ssr` decorator)
176
180
 
177
181
  ```tsx
178
182
  import { useSsrData } from '@riktajs/react';
179
183
 
180
184
  interface PageData {
181
- title: string;
185
+ page: string;
182
186
  items: Array<{ id: string; name: string }>;
183
187
  }
184
188
 
@@ -187,11 +191,15 @@ function ItemList() {
187
191
 
188
192
  if (!ssrData) return <div>Loading...</div>;
189
193
 
194
+ // Access page data
195
+ const { data, title, url } = ssrData;
196
+
190
197
  return (
191
198
  <div>
192
- <h1>{ssrData.data.title}</h1>
199
+ <h1>{title ?? data.page}</h1>
200
+ <p>Current URL: {url}</p>
193
201
  <ul>
194
- {ssrData.data.items.map(item => (
202
+ {data.items.map(item => (
195
203
  <li key={item.id}>{item.name}</li>
196
204
  ))}
197
205
  </ul>
@@ -200,6 +208,31 @@ function ItemList() {
200
208
  }
201
209
  ```
202
210
 
211
+ ### Client-Side Navigation with SSR Data Fetching
212
+
213
+ When navigating client-side using `<Link>` or `navigate()`, `RiktaProvider` automatically fetches the SSR data for the new page from the server. This ensures:
214
+
215
+ 1. **No page flash**: Data is fetched before the route changes
216
+ 2. **Consistent data structure**: Same data shape as initial SSR
217
+ 3. **SEO metadata**: Title and description are updated automatically
218
+
219
+ ```tsx
220
+ // App.tsx - Route based on ssrData.url for data consistency
221
+ function App() {
222
+ const ssrData = useSsrData<{ page: string }>();
223
+
224
+ // Use ssrData.url for routing to ensure data and route are in sync
225
+ const pathname = ssrData?.url?.split('?')[0] ?? '/';
226
+
227
+ return (
228
+ <Layout title={ssrData?.title}>
229
+ {pathname === '/about' && <AboutPage />}
230
+ {pathname === '/' && <HomePage />}
231
+ </Layout>
232
+ );
233
+ }
234
+ ```
235
+
203
236
  ### `useHydration()`
204
237
 
205
238
  Track hydration state for client-only rendering.
package/dist/index.cjs CHANGED
@@ -13,14 +13,26 @@ var defaultRouterContext = {
13
13
  },
14
14
  params: {},
15
15
  setParams: () => {
16
- }
16
+ },
17
+ isNavigating: false
17
18
  };
18
19
  var RouterContext = react.createContext(defaultRouterContext);
19
20
  RouterContext.displayName = "RiktaRouterContext";
20
21
  var SsrContext = react.createContext(null);
21
22
  SsrContext.displayName = "RiktaSsrContext";
22
- function getLocationInfo() {
23
+ function getLocationInfo(ssrUrl) {
23
24
  if (typeof window === "undefined") {
25
+ if (ssrUrl) {
26
+ try {
27
+ const url = new URL(ssrUrl, "http://localhost");
28
+ return {
29
+ pathname: url.pathname,
30
+ search: url.search.slice(1),
31
+ href: ssrUrl
32
+ };
33
+ } catch {
34
+ }
35
+ }
24
36
  return { pathname: "/", search: "", href: "/" };
25
37
  }
26
38
  return {
@@ -32,19 +44,42 @@ function getLocationInfo() {
32
44
  }
33
45
  function getSsrData() {
34
46
  if (typeof window === "undefined") return void 0;
35
- return window.__SSR_DATA__;
47
+ const rawData = window.__SSR_DATA__;
48
+ if (!rawData) return void 0;
49
+ return rawData;
50
+ }
51
+ async function fetchSsrData(url) {
52
+ try {
53
+ const response = await fetch(url, {
54
+ headers: {
55
+ "X-Rikta-Data": "1",
56
+ "Accept": "application/json"
57
+ }
58
+ });
59
+ if (response.ok) {
60
+ const data = await response.json();
61
+ return data;
62
+ }
63
+ } catch (error) {
64
+ console.warn("[RiktaReact] Failed to fetch SSR data:", error);
65
+ }
66
+ return null;
36
67
  }
37
68
  var RiktaProvider = ({
38
69
  ssrData: initialSsrData,
39
70
  initialParams = {},
40
71
  children
41
72
  }) => {
42
- const [ssrData] = react.useState(() => {
73
+ const [ssrData, setSsrData] = react.useState(() => {
43
74
  return initialSsrData ?? getSsrData() ?? null;
44
75
  });
45
- const [location, setLocation] = react.useState(getLocationInfo);
76
+ const [isNavigating, setIsNavigating] = react.useState(false);
77
+ const [location, setLocation] = react.useState(() => {
78
+ const resolvedSsrData = initialSsrData ?? getSsrData();
79
+ return getLocationInfo(resolvedSsrData?.url);
80
+ });
46
81
  const [params, setParams] = react.useState(initialParams);
47
- const navigate = react.useCallback((url, options = {}) => {
82
+ const navigate = react.useCallback(async (url, options = {}) => {
48
83
  const { replace = false, scroll = true, state } = options;
49
84
  if (typeof window === "undefined") return;
50
85
  let targetUrl;
@@ -58,6 +93,11 @@ var RiktaProvider = ({
58
93
  window.location.href = url;
59
94
  return;
60
95
  }
96
+ setIsNavigating(true);
97
+ const newSsrData = await fetchSsrData(targetUrl.href);
98
+ if (newSsrData) {
99
+ setSsrData(newSsrData);
100
+ }
61
101
  if (replace) {
62
102
  window.history.replaceState(state ?? null, "", targetUrl.href);
63
103
  } else {
@@ -71,12 +111,19 @@ var RiktaProvider = ({
71
111
  if (scroll) {
72
112
  window.scrollTo(0, 0);
73
113
  }
74
- window.dispatchEvent(new PopStateEvent("popstate", { state }));
114
+ setIsNavigating(false);
75
115
  }, []);
76
116
  react.useEffect(() => {
77
117
  if (typeof window === "undefined") return;
78
- const handlePopState = () => {
79
- setLocation(getLocationInfo());
118
+ const handlePopState = async () => {
119
+ const newLocation = getLocationInfo();
120
+ setIsNavigating(true);
121
+ const newSsrData = await fetchSsrData(newLocation.href);
122
+ if (newSsrData) {
123
+ setSsrData(newSsrData);
124
+ }
125
+ setLocation(newLocation);
126
+ setIsNavigating(false);
80
127
  };
81
128
  window.addEventListener("popstate", handlePopState);
82
129
  return () => window.removeEventListener("popstate", handlePopState);
@@ -87,8 +134,9 @@ var RiktaProvider = ({
87
134
  href: location.href,
88
135
  navigate,
89
136
  params,
90
- setParams
91
- }), [location.pathname, location.search, location.href, navigate, params]);
137
+ setParams,
138
+ isNavigating
139
+ }), [location.pathname, location.search, location.href, navigate, params, isNavigating]);
92
140
  return /* @__PURE__ */ jsxRuntime.jsx(SsrContext.Provider, { value: ssrData, children: /* @__PURE__ */ jsxRuntime.jsx(RouterContext.Provider, { value: routerValue, children }) });
93
141
  };
94
142
  RiktaProvider.displayName = "RiktaProvider";
@@ -196,8 +244,8 @@ function useFetch(url, options = {}) {
196
244
  });
197
245
  const mountedRef = react.useRef(true);
198
246
  const fetchIdRef = react.useRef(0);
199
- const fetchData = react.useCallback(async () => {
200
- if (skip) return;
247
+ const doFetch = react.useCallback(async (ignoreSkip = false) => {
248
+ if (skip && !ignoreSkip) return;
201
249
  const fetchId = ++fetchIdRef.current;
202
250
  setState((prev) => ({ ...prev, loading: true, error: null }));
203
251
  try {
@@ -221,14 +269,14 @@ function useFetch(url, options = {}) {
221
269
  }, [url, skip, JSON.stringify(fetchOptions), transform]);
222
270
  react.useEffect(() => {
223
271
  mountedRef.current = true;
224
- fetchData();
272
+ doFetch();
225
273
  return () => {
226
274
  mountedRef.current = false;
227
275
  };
228
- }, [fetchData, ...deps]);
276
+ }, [doFetch, ...deps]);
229
277
  const refetch = react.useCallback(async () => {
230
- await fetchData();
231
- }, [fetchData]);
278
+ await doFetch(true);
279
+ }, [doFetch]);
232
280
  return {
233
281
  ...state,
234
282
  refetch
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/context/RouterContext.ts","../src/context/SsrContext.ts","../src/components/RiktaProvider.tsx","../src/hooks/useNavigation.ts","../src/components/Link.tsx","../src/hooks/useParams.ts","../src/hooks/useSearchParams.ts","../src/hooks/useLocation.ts","../src/hooks/useSsrData.ts","../src/hooks/useHydration.ts","../src/hooks/useFetch.ts","../src/hooks/useAction.ts"],"names":["createContext","useState","useCallback","useEffect","useMemo","jsx","useContext","useRef","actionResult"],"mappings":"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","file":"index.cjs","sourcesContent":["import { createContext } from 'react';\nimport type { RouterContextValue } from '../types.js';\n\n/**\n * Default router context value for when no provider is present\n */\nconst defaultRouterContext: RouterContextValue = {\n pathname: '/',\n search: '',\n href: '/',\n navigate: () => {\n console.warn('[RiktaReact] Router context not initialized. Wrap your app with <RiktaProvider>');\n },\n params: {},\n setParams: () => {},\n};\n\n/**\n * React context for router state\n * Provides navigation utilities and current location info\n */\nexport const RouterContext = createContext<RouterContextValue>(defaultRouterContext);\n\nRouterContext.displayName = 'RiktaRouterContext';\n","import { createContext } from 'react';\nimport type { SsrData } from '../types.js';\n\n/**\n * React context for SSR data\n * Holds the server-rendered data passed via window.__SSR_DATA__\n */\nexport const SsrContext = createContext<SsrData | null>(null);\n\nSsrContext.displayName = 'RiktaSsrContext';\n","import { useState, useCallback, useEffect, useMemo, type FC } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\nimport { SsrContext } from '../context/SsrContext.js';\nimport type { RiktaProviderProps, SsrData, NavigateOptions } from '../types.js';\n\n/**\n * Get current location from window or fallback for SSR\n */\nfunction getLocationInfo() {\n if (typeof window === 'undefined') {\n return { pathname: '/', search: '', href: '/' };\n }\n return {\n pathname: window.location.pathname,\n search: window.location.search.slice(1), // Remove leading ?\n href: window.location.href,\n };\n}\n\n/**\n * Get SSR data from window\n */\nfunction getSsrData(): SsrData | undefined {\n if (typeof window === 'undefined') return undefined;\n return window.__SSR_DATA__;\n}\n\n/**\n * RiktaProvider - Main provider component for Rikta React utilities\n * \n * Provides routing context, SSR data, and navigation utilities to the app.\n * \n * @example\n * ```tsx\n * // In entry-client.tsx\n * import { RiktaProvider } from '@riktajs/react';\n * \n * hydrateRoot(\n * document.getElementById('root')!,\n * <RiktaProvider>\n * <App />\n * </RiktaProvider>\n * );\n * ```\n */\nexport const RiktaProvider: FC<RiktaProviderProps> = ({\n ssrData: initialSsrData,\n initialParams = {},\n children,\n}) => {\n // Initialize SSR data from window or props\n const [ssrData] = useState<SsrData | null>(() => {\n return initialSsrData ?? getSsrData() ?? null;\n });\n\n // Initialize location state\n const [location, setLocation] = useState(getLocationInfo);\n \n // Route params state\n const [params, setParams] = useState<Record<string, string>>(initialParams);\n\n /**\n * Navigate to a new URL using History API\n */\n const navigate = useCallback((url: string, options: NavigateOptions = {}) => {\n const { replace = false, scroll = true, state } = options;\n\n if (typeof window === 'undefined') return;\n\n // Handle full URLs vs relative paths\n let targetUrl: URL;\n try {\n targetUrl = new URL(url, window.location.origin);\n } catch {\n console.error(`[RiktaReact] Invalid URL: ${url}`);\n return;\n }\n\n // Only handle same-origin navigation\n if (targetUrl.origin !== window.location.origin) {\n window.location.href = url;\n return;\n }\n\n // Update history\n if (replace) {\n window.history.replaceState(state ?? null, '', targetUrl.href);\n } else {\n window.history.pushState(state ?? null, '', targetUrl.href);\n }\n\n // Update location state\n setLocation({\n pathname: targetUrl.pathname,\n search: targetUrl.search.slice(1),\n href: targetUrl.href,\n });\n\n // Scroll to top if requested\n if (scroll) {\n window.scrollTo(0, 0);\n }\n\n // Dispatch popstate event for any other listeners\n window.dispatchEvent(new PopStateEvent('popstate', { state }));\n }, []);\n\n // Listen for popstate (browser back/forward)\n useEffect(() => {\n if (typeof window === 'undefined') return;\n\n const handlePopState = () => {\n setLocation(getLocationInfo());\n };\n\n window.addEventListener('popstate', handlePopState);\n return () => window.removeEventListener('popstate', handlePopState);\n }, []);\n\n // Memoize router context value\n const routerValue = useMemo(() => ({\n pathname: location.pathname,\n search: location.search,\n href: location.href,\n navigate,\n params,\n setParams,\n }), [location.pathname, location.search, location.href, navigate, params]);\n\n return (\n <SsrContext.Provider value={ssrData}>\n <RouterContext.Provider value={routerValue}>\n {children}\n </RouterContext.Provider>\n </SsrContext.Provider>\n );\n};\n\nRiktaProvider.displayName = 'RiktaProvider';\n","import { useContext, useCallback } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\nimport type { NavigateOptions } from '../types.js';\n\n/**\n * Hook for programmatic navigation\n * \n * @returns Object with navigate function and current location info\n * \n * @example\n * ```tsx\n * import { useNavigation } from '@riktajs/react';\n * \n * function MyComponent() {\n * const { navigate, pathname } = useNavigation();\n * \n * const handleSubmit = async () => {\n * await saveData();\n * navigate('/success');\n * };\n * \n * return (\n * <button onClick={handleSubmit}>\n * Submit (current path: {pathname})\n * </button>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // With options\n * const { navigate } = useNavigation();\n * \n * // Replace history entry (for redirects)\n * navigate('/login', { replace: true });\n * \n * // Don't scroll to top\n * navigate('/next', { scroll: false });\n * \n * // Pass state\n * navigate('/edit', { state: { from: 'list' } });\n * ```\n */\nexport function useNavigation() {\n const context = useContext(RouterContext);\n\n const navigate = useCallback(\n (url: string, options?: NavigateOptions) => {\n context.navigate(url, options);\n },\n [context.navigate]\n );\n\n return {\n /** Navigate to a new URL */\n navigate,\n /** Current pathname */\n pathname: context.pathname,\n /** Current search string (without ?) */\n search: context.search,\n /** Full href */\n href: context.href,\n };\n}\n","import { useCallback, type FC, type MouseEvent } from 'react';\nimport { useNavigation } from '../hooks/useNavigation.js';\nimport type { LinkProps } from '../types.js';\n\n/**\n * Link component for client-side navigation\n * \n * Renders an anchor tag that uses the History API for navigation\n * instead of causing a full page reload.\n * \n * @example\n * ```tsx\n * import { Link } from '@riktajs/react';\n * \n * function Nav() {\n * return (\n * <nav>\n * <Link href=\"/\">Home</Link>\n * <Link href=\"/about\">About</Link>\n * <Link href=\"/items/123\">Item 123</Link>\n * </nav>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // With options\n * <Link href=\"/dashboard\" replace scroll={false}>\n * Dashboard\n * </Link>\n * ```\n */\nexport const Link: FC<LinkProps> = ({\n href,\n replace = false,\n scroll = true,\n prefetch = false,\n state,\n children,\n onClick,\n ...restProps\n}) => {\n const { navigate } = useNavigation();\n\n const handleClick = useCallback(\n (e: MouseEvent<HTMLAnchorElement>) => {\n // Call user's onClick handler if provided\n onClick?.(e);\n\n // Don't handle if default was prevented\n if (e.defaultPrevented) return;\n\n // Don't handle modified clicks (open in new tab, etc.)\n if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;\n\n // Don't handle right clicks\n if (e.button !== 0) return;\n\n // Don't handle target=\"_blank\" etc.\n const target = (e.currentTarget as HTMLAnchorElement).target;\n if (target && target !== '_self') return;\n\n // Don't handle external links\n try {\n const url = new URL(href, window.location.origin);\n if (url.origin !== window.location.origin) return;\n } catch {\n return;\n }\n\n // Handle the navigation\n e.preventDefault();\n navigate(href, { replace, scroll, state });\n },\n [href, replace, scroll, state, navigate, onClick]\n );\n\n // Prefetch support could be added here in the future\n // using <link rel=\"prefetch\"> or Intersection Observer\n\n return (\n <a href={href} onClick={handleClick} {...restProps}>\n {children}\n </a>\n );\n};\n\nLink.displayName = 'Link';\n","import { useContext } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\n\n/**\n * Hook to access route parameters\n * \n * Route parameters are extracted from the URL path by the server\n * and passed via SSR data. They're stored in the RouterContext.\n * \n * @returns Object with route parameter values\n * \n * @example\n * ```tsx\n * // For route /item/:id\n * import { useParams } from '@riktajs/react';\n * \n * function ItemPage() {\n * const { id } = useParams<{ id: string }>();\n * \n * return <h1>Item {id}</h1>;\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Multiple params - /users/:userId/posts/:postId\n * function PostPage() {\n * const { userId, postId } = useParams<{ userId: string; postId: string }>();\n * \n * return <h1>Post {postId} by User {userId}</h1>;\n * }\n * ```\n */\nexport function useParams<T extends Record<string, string> = Record<string, string>>(): T {\n const context = useContext(RouterContext);\n return context.params as T;\n}\n","import { useContext, useMemo } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\n\n/**\n * Hook to access and manipulate URL search parameters\n * \n * @returns Tuple of [URLSearchParams, setSearchParams function]\n * \n * @example\n * ```tsx\n * import { useSearchParams } from '@riktajs/react';\n * \n * function SearchPage() {\n * const [searchParams, setSearchParams] = useSearchParams();\n * const query = searchParams.get('q') ?? '';\n * const page = parseInt(searchParams.get('page') ?? '1', 10);\n * \n * const handleSearch = (newQuery: string) => {\n * setSearchParams({ q: newQuery, page: '1' });\n * };\n * \n * const handleNextPage = () => {\n * setSearchParams({ q: query, page: String(page + 1) });\n * };\n * \n * return (\n * <div>\n * <input \n * value={query} \n * onChange={(e) => handleSearch(e.target.value)} \n * />\n * <button onClick={handleNextPage}>Next Page</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useSearchParams(): [URLSearchParams, (params: Record<string, string> | URLSearchParams) => void] {\n const context = useContext(RouterContext);\n\n const searchParams = useMemo(() => {\n return new URLSearchParams(context.search);\n }, [context.search]);\n\n const setSearchParams = useMemo(() => {\n return (params: Record<string, string> | URLSearchParams) => {\n const newParams = params instanceof URLSearchParams \n ? params \n : new URLSearchParams(params);\n \n const search = newParams.toString();\n const newUrl = search \n ? `${context.pathname}?${search}` \n : context.pathname;\n \n context.navigate(newUrl, { scroll: false });\n };\n }, [context.pathname, context.navigate]);\n\n return [searchParams, setSearchParams];\n}\n","import { useContext } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\n\n/**\n * Location object returned by useLocation\n */\nexport interface Location {\n /** Current pathname (e.g., /items/123) */\n pathname: string;\n /** Current search string without ? (e.g., page=2&sort=asc) */\n search: string;\n /** Full href */\n href: string;\n /** Parsed search params */\n searchParams: URLSearchParams;\n}\n\n/**\n * Hook to access current location information\n * \n * @returns Location object with pathname, search, href, and searchParams\n * \n * @example\n * ```tsx\n * import { useLocation } from '@riktajs/react';\n * \n * function Breadcrumbs() {\n * const location = useLocation();\n * \n * return (\n * <nav>\n * Current path: {location.pathname}\n * {location.search && <span>?{location.search}</span>}\n * </nav>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Access search params\n * function FilterDisplay() {\n * const { searchParams } = useLocation();\n * const filter = searchParams.get('filter');\n * \n * return filter ? <span>Filtered by: {filter}</span> : null;\n * }\n * ```\n */\nexport function useLocation(): Location {\n const context = useContext(RouterContext);\n\n return {\n pathname: context.pathname,\n search: context.search,\n href: context.href,\n searchParams: new URLSearchParams(context.search),\n };\n}\n","import { useContext } from 'react';\nimport { SsrContext } from '../context/SsrContext.js';\nimport type { SsrData } from '../types.js';\n\n/**\n * Hook to access SSR data passed from server\n * \n * SSR data is passed via window.__SSR_DATA__ and contains\n * the initial data rendered on the server.\n * \n * @returns SSR data object or null if not available\n * \n * @example\n * ```tsx\n * import { useSsrData } from '@riktajs/react';\n * \n * interface PageData {\n * title: string;\n * items: Array<{ id: string; name: string }>;\n * }\n * \n * function ItemList() {\n * const ssrData = useSsrData<PageData>();\n * \n * if (!ssrData) {\n * return <div>Loading...</div>;\n * }\n * \n * return (\n * <div>\n * <h1>{ssrData.data.title}</h1>\n * <ul>\n * {ssrData.data.items.map(item => (\n * <li key={item.id}>{item.name}</li>\n * ))}\n * </ul>\n * </div>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Access just the data\n * function MyComponent() {\n * const ssrData = useSsrData<{ user: User }>();\n * const user = ssrData?.data.user;\n * \n * return user ? <UserProfile user={user} /> : <LoginPrompt />;\n * }\n * ```\n */\nexport function useSsrData<T = unknown>(): SsrData<T> | null {\n const context = useContext(SsrContext);\n return context as SsrData<T> | null;\n}\n","import { useState, useEffect } from 'react';\nimport type { HydrationState } from '../types.js';\n\n/**\n * Hook to track hydration state\n * \n * Useful for rendering different content during SSR vs after hydration,\n * or for avoiding hydration mismatches.\n * \n * @returns Hydration state object\n * \n * @example\n * ```tsx\n * import { useHydration } from '@riktajs/react';\n * \n * function TimeDisplay() {\n * const { isHydrated, isServer } = useHydration();\n * \n * // On server and initial render, show static content\n * // After hydration, show dynamic content\n * if (!isHydrated) {\n * return <span>Loading time...</span>;\n * }\n * \n * return <span>{new Date().toLocaleTimeString()}</span>;\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Avoid hydration mismatch with client-only content\n * function ClientOnlyComponent() {\n * const { isHydrated } = useHydration();\n * \n * if (!isHydrated) {\n * return null; // Or a placeholder\n * }\n * \n * return <SomeClientOnlyLibrary />;\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Conditional rendering based on environment\n * function DebugPanel() {\n * const { isServer } = useHydration();\n * \n * // Never render on server, only after client hydration\n * if (isServer) return null;\n * \n * return <DevTools />;\n * }\n * ```\n */\nexport function useHydration(): HydrationState {\n const isServer = typeof window === 'undefined';\n const [isHydrated, setIsHydrated] = useState(false);\n\n useEffect(() => {\n setIsHydrated(true);\n }, []);\n\n return {\n isHydrated,\n isServer,\n };\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport type { FetchState } from '../types.js';\n\n/**\n * Options for useFetch hook\n */\nexport interface UseFetchOptions extends Omit<RequestInit, 'body'> {\n /** Skip initial fetch (useful for conditional fetching) */\n skip?: boolean;\n /** Dependencies that trigger refetch when changed */\n deps?: unknown[];\n /** Transform response before setting data */\n transform?: (data: unknown) => unknown;\n}\n\n/**\n * Hook for data fetching with loading and error states\n * \n * @param url URL to fetch from\n * @param options Fetch options\n * @returns Fetch state with data, loading, error, and refetch function\n * \n * @example\n * ```tsx\n * import { useFetch } from '@riktajs/react';\n * \n * interface User {\n * id: string;\n * name: string;\n * }\n * \n * function UserProfile({ userId }: { userId: string }) {\n * const { data, loading, error, refetch } = useFetch<User>(\n * `/api/users/${userId}`\n * );\n * \n * if (loading) return <Spinner />;\n * if (error) return <Error message={error} />;\n * if (!data) return null;\n * \n * return (\n * <div>\n * <h1>{data.name}</h1>\n * <button onClick={refetch}>Refresh</button>\n * </div>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // With options\n * const { data } = useFetch<Item[]>('/api/items', {\n * headers: { 'Authorization': `Bearer ${token}` },\n * deps: [token], // Refetch when token changes\n * skip: !token, // Don't fetch until we have a token\n * });\n * ```\n * \n * @example\n * ```tsx\n * // With transform\n * const { data } = useFetch<{ results: Item[] }>('/api/search', {\n * transform: (res) => res.results, // Extract just the results array\n * });\n * ```\n */\nexport function useFetch<T = unknown>(\n url: string,\n options: UseFetchOptions = {}\n): FetchState<T> {\n const { skip = false, deps = [], transform, ...fetchOptions } = options;\n \n const [state, setState] = useState<Omit<FetchState<T>, 'refetch'>>({\n data: null,\n loading: !skip,\n error: null,\n });\n\n // Use ref to track if component is mounted\n const mountedRef = useRef(true);\n // Use ref to track current fetch to handle race conditions\n const fetchIdRef = useRef(0);\n\n const fetchData = useCallback(async () => {\n if (skip) return;\n\n const fetchId = ++fetchIdRef.current;\n setState(prev => ({ ...prev, loading: true, error: null }));\n\n try {\n const response = await fetch(url, fetchOptions);\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n let data = await response.json();\n\n // Apply transform if provided\n if (transform) {\n data = transform(data);\n }\n\n // Only update state if this is still the current fetch and component is mounted\n if (fetchId === fetchIdRef.current && mountedRef.current) {\n setState({ data: data as T, loading: false, error: null });\n }\n } catch (err) {\n if (fetchId === fetchIdRef.current && mountedRef.current) {\n const message = err instanceof Error ? err.message : 'An error occurred';\n setState({ data: null, loading: false, error: message });\n }\n }\n }, [url, skip, JSON.stringify(fetchOptions), transform]);\n\n // Fetch on mount and when dependencies change\n useEffect(() => {\n mountedRef.current = true;\n fetchData();\n\n return () => {\n mountedRef.current = false;\n };\n }, [fetchData, ...deps]);\n\n const refetch = useCallback(async () => {\n await fetchData();\n }, [fetchData]);\n\n return {\n ...state,\n refetch,\n };\n}\n","import { useState, useCallback } from 'react';\nimport type { ActionResult, ActionState } from '../types.js';\n\n/**\n * Options for useAction hook\n */\nexport interface UseActionOptions<TResult = unknown> {\n /** Callback on successful action */\n onSuccess?: (result: TResult) => void;\n /** Callback on action error */\n onError?: (error: string) => void;\n /** HTTP method to use */\n method?: 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n /** Additional headers */\n headers?: Record<string, string>;\n}\n\n/**\n * Hook for executing server actions (form submissions, mutations)\n * \n * @param url URL to send the action to\n * @param options Action options\n * @returns Action state with execute, pending, result, and reset\n * \n * @example\n * ```tsx\n * import { useAction } from '@riktajs/react';\n * \n * interface CreateItemInput {\n * name: string;\n * price: number;\n * }\n * \n * interface Item {\n * id: string;\n * name: string;\n * price: number;\n * }\n * \n * function CreateItemForm() {\n * const { execute, pending, result } = useAction<CreateItemInput, Item>(\n * '/api/items',\n * {\n * onSuccess: (item) => {\n * console.log('Created item:', item);\n * },\n * }\n * );\n * \n * const handleSubmit = async (e: FormEvent) => {\n * e.preventDefault();\n * const formData = new FormData(e.target as HTMLFormElement);\n * await execute({\n * name: formData.get('name') as string,\n * price: Number(formData.get('price')),\n * });\n * };\n * \n * return (\n * <form onSubmit={handleSubmit}>\n * <input name=\"name\" required />\n * <input name=\"price\" type=\"number\" required />\n * <button disabled={pending}>\n * {pending ? 'Creating...' : 'Create Item'}\n * </button>\n * {result?.error && <p className=\"error\">{result.error}</p>}\n * {result?.fieldErrors?.name && (\n * <p className=\"error\">{result.fieldErrors.name[0]}</p>\n * )}\n * </form>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // DELETE action\n * const { execute, pending } = useAction<{ id: string }, void>(\n * '/api/items',\n * { method: 'DELETE' }\n * );\n * \n * const handleDelete = () => execute({ id: itemId });\n * ```\n */\nexport function useAction<TInput = unknown, TResult = unknown>(\n url: string,\n options: UseActionOptions<TResult> = {}\n): ActionState<TInput, TResult> {\n const {\n onSuccess,\n onError,\n method = 'POST',\n headers: customHeaders = {},\n } = options;\n\n const [pending, setPending] = useState(false);\n const [result, setResult] = useState<ActionResult<TResult> | null>(null);\n\n const execute = useCallback(\n async (input: TInput): Promise<ActionResult<TResult>> => {\n setPending(true);\n setResult(null);\n\n try {\n const response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n ...customHeaders,\n },\n body: JSON.stringify(input),\n });\n\n const data = await response.json();\n\n if (!response.ok) {\n // Handle error response\n const actionResult: ActionResult<TResult> = {\n success: false,\n error: data.message || data.error || `HTTP ${response.status}`,\n fieldErrors: data.fieldErrors,\n };\n setResult(actionResult);\n onError?.(actionResult.error!);\n return actionResult;\n }\n\n // Handle success response\n const actionResult: ActionResult<TResult> = {\n success: true,\n data: data as TResult,\n };\n setResult(actionResult);\n onSuccess?.(data as TResult);\n return actionResult;\n } catch (err) {\n const message = err instanceof Error ? err.message : 'An error occurred';\n const actionResult: ActionResult<TResult> = {\n success: false,\n error: message,\n };\n setResult(actionResult);\n onError?.(message);\n return actionResult;\n } finally {\n setPending(false);\n }\n },\n [url, method, JSON.stringify(customHeaders), onSuccess, onError]\n );\n\n const reset = useCallback(() => {\n setResult(null);\n }, []);\n\n return {\n execute,\n pending,\n result,\n reset,\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/context/RouterContext.ts","../src/context/SsrContext.ts","../src/components/RiktaProvider.tsx","../src/hooks/useNavigation.ts","../src/components/Link.tsx","../src/hooks/useParams.ts","../src/hooks/useSearchParams.ts","../src/hooks/useLocation.ts","../src/hooks/useSsrData.ts","../src/hooks/useHydration.ts","../src/hooks/useFetch.ts","../src/hooks/useAction.ts"],"names":["createContext","useState","useCallback","useEffect","useMemo","jsx","useContext","useRef","actionResult"],"mappings":"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","file":"index.cjs","sourcesContent":["import { createContext } from 'react';\nimport type { RouterContextValue } from '../types.js';\n\n/**\n * Default router context value for when no provider is present\n */\nconst defaultRouterContext: RouterContextValue = {\n pathname: '/',\n search: '',\n href: '/',\n navigate: () => {\n console.warn('[RiktaReact] Router context not initialized. Wrap your app with <RiktaProvider>');\n },\n params: {},\n setParams: () => {},\n isNavigating: false,\n};\n\n/**\n * React context for router state\n * Provides navigation utilities and current location info\n */\nexport const RouterContext = createContext<RouterContextValue>(defaultRouterContext);\n\nRouterContext.displayName = 'RiktaRouterContext';\n","import { createContext } from 'react';\nimport type { SsrData } from '../types.js';\n\n/**\n * React context for SSR data\n * Holds the server-rendered data passed via window.__SSR_DATA__\n */\nexport const SsrContext = createContext<SsrData | null>(null);\n\nSsrContext.displayName = 'RiktaSsrContext';\n","import { useState, useCallback, useEffect, useMemo, type FC } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\nimport { SsrContext } from '../context/SsrContext.js';\nimport type { RiktaProviderProps, SsrData, NavigateOptions } from '../types.js';\n\n/**\n * Get current location from window or fallback for SSR\n */\nfunction getLocationInfo(ssrUrl?: string) {\n if (typeof window === 'undefined') {\n // During SSR, use the URL from ssrData if available\n if (ssrUrl) {\n try {\n const url = new URL(ssrUrl, 'http://localhost');\n return {\n pathname: url.pathname,\n search: url.search.slice(1),\n href: ssrUrl,\n };\n } catch {\n // Fall through to default\n }\n }\n return { pathname: '/', search: '', href: '/' };\n }\n return {\n pathname: window.location.pathname,\n search: window.location.search.slice(1), // Remove leading ?\n href: window.location.href,\n };\n}\n\n/**\n * Get SSR data from window\n * Server now puts data in normalized format: { data, url, title, description }\n */\nfunction getSsrData(): SsrData | undefined {\n if (typeof window === 'undefined') return undefined;\n \n const rawData = window.__SSR_DATA__ as Record<string, unknown> | undefined;\n if (!rawData) return undefined;\n \n // Data should already be in normalized format from entry-server\n return rawData as unknown as SsrData;\n}\n\n/**\n * Fetch SSR data from server for client-side navigation\n */\nasync function fetchSsrData(url: string): Promise<SsrData | null> {\n try {\n const response = await fetch(url, {\n headers: {\n 'X-Rikta-Data': '1',\n 'Accept': 'application/json',\n },\n });\n \n if (response.ok) {\n const data = await response.json();\n return data as SsrData;\n }\n } catch (error) {\n console.warn('[RiktaReact] Failed to fetch SSR data:', error);\n }\n return null;\n}\n\n/**\n * RiktaProvider - Main provider component for Rikta React utilities\n * \n * Provides routing context, SSR data, and navigation utilities to the app.\n * Automatically fetches new page data during client-side navigation.\n * \n * @example\n * ```tsx\n * // In entry-client.tsx\n * import { RiktaProvider } from '@riktajs/react';\n * \n * hydrateRoot(\n * document.getElementById('root')!,\n * <RiktaProvider>\n * <App />\n * </RiktaProvider>\n * );\n * ```\n */\nexport const RiktaProvider: FC<RiktaProviderProps> = ({\n ssrData: initialSsrData,\n initialParams = {},\n children,\n}) => {\n // Initialize SSR data from window or props\n const [ssrData, setSsrData] = useState<SsrData | null>(() => {\n return initialSsrData ?? getSsrData() ?? null;\n });\n\n // Loading state for navigation\n const [isNavigating, setIsNavigating] = useState(false);\n\n // Initialize location state - use URL from ssrData for SSR consistency\n const [location, setLocation] = useState(() => {\n const resolvedSsrData = initialSsrData ?? getSsrData();\n return getLocationInfo(resolvedSsrData?.url);\n });\n \n // Route params state\n const [params, setParams] = useState<Record<string, string>>(initialParams);\n\n /**\n * Navigate to a new URL using History API with data fetching\n */\n const navigate = useCallback(async (url: string, options: NavigateOptions = {}) => {\n const { replace = false, scroll = true, state } = options;\n\n if (typeof window === 'undefined') return;\n\n // Handle full URLs vs relative paths\n let targetUrl: URL;\n try {\n targetUrl = new URL(url, window.location.origin);\n } catch {\n console.error(`[RiktaReact] Invalid URL: ${url}`);\n return;\n }\n\n // Only handle same-origin navigation\n if (targetUrl.origin !== window.location.origin) {\n window.location.href = url;\n return;\n }\n\n // Start navigation\n setIsNavigating(true);\n\n // Fetch new SSR data from server BEFORE updating anything else\n const newSsrData = await fetchSsrData(targetUrl.href);\n\n // Update SSR data FIRST (before location changes)\n if (newSsrData) {\n setSsrData(newSsrData);\n }\n\n // Update history\n if (replace) {\n window.history.replaceState(state ?? null, '', targetUrl.href);\n } else {\n window.history.pushState(state ?? null, '', targetUrl.href);\n }\n\n // Update location state AFTER data is ready\n setLocation({\n pathname: targetUrl.pathname,\n search: targetUrl.search.slice(1),\n href: targetUrl.href,\n });\n\n // Scroll to top if requested\n if (scroll) {\n window.scrollTo(0, 0);\n }\n\n // End navigation\n setIsNavigating(false);\n }, []);\n\n // Listen for popstate (browser back/forward) and fetch data\n useEffect(() => {\n if (typeof window === 'undefined') return;\n\n const handlePopState = async () => {\n const newLocation = getLocationInfo();\n \n // Fetch data for the new URL FIRST\n setIsNavigating(true);\n const newSsrData = await fetchSsrData(newLocation.href);\n \n // Update SSR data BEFORE location\n if (newSsrData) {\n setSsrData(newSsrData);\n }\n \n // Then update location\n setLocation(newLocation);\n setIsNavigating(false);\n };\n\n window.addEventListener('popstate', handlePopState);\n return () => window.removeEventListener('popstate', handlePopState);\n }, []);\n\n // Memoize router context value\n const routerValue = useMemo(() => ({\n pathname: location.pathname,\n search: location.search,\n href: location.href,\n navigate,\n params,\n setParams,\n isNavigating,\n }), [location.pathname, location.search, location.href, navigate, params, isNavigating]);\n\n return (\n <SsrContext.Provider value={ssrData}>\n <RouterContext.Provider value={routerValue}>\n {children}\n </RouterContext.Provider>\n </SsrContext.Provider>\n );\n};\n\nRiktaProvider.displayName = 'RiktaProvider';\n","import { useContext, useCallback } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\nimport type { NavigateOptions } from '../types.js';\n\n/**\n * Hook for programmatic navigation\n * \n * @returns Object with navigate function and current location info\n * \n * @example\n * ```tsx\n * import { useNavigation } from '@riktajs/react';\n * \n * function MyComponent() {\n * const { navigate, pathname } = useNavigation();\n * \n * const handleSubmit = async () => {\n * await saveData();\n * navigate('/success');\n * };\n * \n * return (\n * <button onClick={handleSubmit}>\n * Submit (current path: {pathname})\n * </button>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // With options\n * const { navigate } = useNavigation();\n * \n * // Replace history entry (for redirects)\n * navigate('/login', { replace: true });\n * \n * // Don't scroll to top\n * navigate('/next', { scroll: false });\n * \n * // Pass state\n * navigate('/edit', { state: { from: 'list' } });\n * ```\n */\nexport function useNavigation() {\n const context = useContext(RouterContext);\n\n const navigate = useCallback(\n (url: string, options?: NavigateOptions) => {\n context.navigate(url, options);\n },\n [context.navigate]\n );\n\n return {\n /** Navigate to a new URL */\n navigate,\n /** Current pathname */\n pathname: context.pathname,\n /** Current search string (without ?) */\n search: context.search,\n /** Full href */\n href: context.href,\n };\n}\n","import { useCallback, type FC, type MouseEvent } from 'react';\nimport { useNavigation } from '../hooks/useNavigation.js';\nimport type { LinkProps } from '../types.js';\n\n/**\n * Link component for client-side navigation\n * \n * Renders an anchor tag that uses the History API for navigation\n * instead of causing a full page reload.\n * \n * @example\n * ```tsx\n * import { Link } from '@riktajs/react';\n * \n * function Nav() {\n * return (\n * <nav>\n * <Link href=\"/\">Home</Link>\n * <Link href=\"/about\">About</Link>\n * <Link href=\"/items/123\">Item 123</Link>\n * </nav>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // With options\n * <Link href=\"/dashboard\" replace scroll={false}>\n * Dashboard\n * </Link>\n * ```\n */\nexport const Link: FC<LinkProps> = ({\n href,\n replace = false,\n scroll = true,\n prefetch = false,\n state,\n children,\n onClick,\n ...restProps\n}) => {\n const { navigate } = useNavigation();\n\n const handleClick = useCallback(\n (e: MouseEvent<HTMLAnchorElement>) => {\n // Call user's onClick handler if provided\n onClick?.(e);\n\n // Don't handle if default was prevented\n if (e.defaultPrevented) return;\n\n // Don't handle modified clicks (open in new tab, etc.)\n if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;\n\n // Don't handle right clicks\n if (e.button !== 0) return;\n\n // Don't handle target=\"_blank\" etc.\n const target = (e.currentTarget as HTMLAnchorElement).target;\n if (target && target !== '_self') return;\n\n // Don't handle external links\n try {\n const url = new URL(href, window.location.origin);\n if (url.origin !== window.location.origin) return;\n } catch {\n return;\n }\n\n // Handle the navigation\n e.preventDefault();\n navigate(href, { replace, scroll, state });\n },\n [href, replace, scroll, state, navigate, onClick]\n );\n\n // Prefetch support could be added here in the future\n // using <link rel=\"prefetch\"> or Intersection Observer\n\n return (\n <a href={href} onClick={handleClick} {...restProps}>\n {children}\n </a>\n );\n};\n\nLink.displayName = 'Link';\n","import { useContext } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\n\n/**\n * Hook to access route parameters\n * \n * Route parameters are extracted from the URL path by the server\n * and passed via SSR data. They're stored in the RouterContext.\n * \n * @returns Object with route parameter values\n * \n * @example\n * ```tsx\n * // For route /item/:id\n * import { useParams } from '@riktajs/react';\n * \n * function ItemPage() {\n * const { id } = useParams<{ id: string }>();\n * \n * return <h1>Item {id}</h1>;\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Multiple params - /users/:userId/posts/:postId\n * function PostPage() {\n * const { userId, postId } = useParams<{ userId: string; postId: string }>();\n * \n * return <h1>Post {postId} by User {userId}</h1>;\n * }\n * ```\n */\nexport function useParams<T extends Record<string, string> = Record<string, string>>(): T {\n const context = useContext(RouterContext);\n return context.params as T;\n}\n","import { useContext, useMemo } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\n\n/**\n * Hook to access and manipulate URL search parameters\n * \n * @returns Tuple of [URLSearchParams, setSearchParams function]\n * \n * @example\n * ```tsx\n * import { useSearchParams } from '@riktajs/react';\n * \n * function SearchPage() {\n * const [searchParams, setSearchParams] = useSearchParams();\n * const query = searchParams.get('q') ?? '';\n * const page = parseInt(searchParams.get('page') ?? '1', 10);\n * \n * const handleSearch = (newQuery: string) => {\n * setSearchParams({ q: newQuery, page: '1' });\n * };\n * \n * const handleNextPage = () => {\n * setSearchParams({ q: query, page: String(page + 1) });\n * };\n * \n * return (\n * <div>\n * <input \n * value={query} \n * onChange={(e) => handleSearch(e.target.value)} \n * />\n * <button onClick={handleNextPage}>Next Page</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useSearchParams(): [URLSearchParams, (params: Record<string, string> | URLSearchParams) => void] {\n const context = useContext(RouterContext);\n\n const searchParams = useMemo(() => {\n return new URLSearchParams(context.search);\n }, [context.search]);\n\n const setSearchParams = useMemo(() => {\n return (params: Record<string, string> | URLSearchParams) => {\n const newParams = params instanceof URLSearchParams \n ? params \n : new URLSearchParams(params);\n \n const search = newParams.toString();\n const newUrl = search \n ? `${context.pathname}?${search}` \n : context.pathname;\n \n context.navigate(newUrl, { scroll: false });\n };\n }, [context.pathname, context.navigate]);\n\n return [searchParams, setSearchParams];\n}\n","import { useContext } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\n\n/**\n * Location object returned by useLocation\n */\nexport interface Location {\n /** Current pathname (e.g., /items/123) */\n pathname: string;\n /** Current search string without ? (e.g., page=2&sort=asc) */\n search: string;\n /** Full href */\n href: string;\n /** Parsed search params */\n searchParams: URLSearchParams;\n}\n\n/**\n * Hook to access current location information\n * \n * @returns Location object with pathname, search, href, and searchParams\n * \n * @example\n * ```tsx\n * import { useLocation } from '@riktajs/react';\n * \n * function Breadcrumbs() {\n * const location = useLocation();\n * \n * return (\n * <nav>\n * Current path: {location.pathname}\n * {location.search && <span>?{location.search}</span>}\n * </nav>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Access search params\n * function FilterDisplay() {\n * const { searchParams } = useLocation();\n * const filter = searchParams.get('filter');\n * \n * return filter ? <span>Filtered by: {filter}</span> : null;\n * }\n * ```\n */\nexport function useLocation(): Location {\n const context = useContext(RouterContext);\n\n return {\n pathname: context.pathname,\n search: context.search,\n href: context.href,\n searchParams: new URLSearchParams(context.search),\n };\n}\n","import { useContext } from 'react';\nimport { SsrContext } from '../context/SsrContext.js';\nimport type { SsrData } from '../types.js';\n\n/**\n * Hook to access SSR data passed from server\n * \n * SSR data is passed via window.__SSR_DATA__ and contains\n * the initial data rendered on the server.\n * \n * @returns SSR data object or null if not available\n * \n * @example\n * ```tsx\n * import { useSsrData } from '@riktajs/react';\n * \n * interface PageData {\n * title: string;\n * items: Array<{ id: string; name: string }>;\n * }\n * \n * function ItemList() {\n * const ssrData = useSsrData<PageData>();\n * \n * if (!ssrData) {\n * return <div>Loading...</div>;\n * }\n * \n * return (\n * <div>\n * <h1>{ssrData.data.title}</h1>\n * <ul>\n * {ssrData.data.items.map(item => (\n * <li key={item.id}>{item.name}</li>\n * ))}\n * </ul>\n * </div>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Access just the data\n * function MyComponent() {\n * const ssrData = useSsrData<{ user: User }>();\n * const user = ssrData?.data.user;\n * \n * return user ? <UserProfile user={user} /> : <LoginPrompt />;\n * }\n * ```\n */\nexport function useSsrData<T = unknown>(): SsrData<T> | null {\n const context = useContext(SsrContext);\n return context as SsrData<T> | null;\n}\n","import { useState, useEffect } from 'react';\nimport type { HydrationState } from '../types.js';\n\n/**\n * Hook to track hydration state\n * \n * Useful for rendering different content during SSR vs after hydration,\n * or for avoiding hydration mismatches.\n * \n * @returns Hydration state object\n * \n * @example\n * ```tsx\n * import { useHydration } from '@riktajs/react';\n * \n * function TimeDisplay() {\n * const { isHydrated, isServer } = useHydration();\n * \n * // On server and initial render, show static content\n * // After hydration, show dynamic content\n * if (!isHydrated) {\n * return <span>Loading time...</span>;\n * }\n * \n * return <span>{new Date().toLocaleTimeString()}</span>;\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Avoid hydration mismatch with client-only content\n * function ClientOnlyComponent() {\n * const { isHydrated } = useHydration();\n * \n * if (!isHydrated) {\n * return null; // Or a placeholder\n * }\n * \n * return <SomeClientOnlyLibrary />;\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Conditional rendering based on environment\n * function DebugPanel() {\n * const { isServer } = useHydration();\n * \n * // Never render on server, only after client hydration\n * if (isServer) return null;\n * \n * return <DevTools />;\n * }\n * ```\n */\nexport function useHydration(): HydrationState {\n const isServer = typeof window === 'undefined';\n const [isHydrated, setIsHydrated] = useState(false);\n\n useEffect(() => {\n setIsHydrated(true);\n }, []);\n\n return {\n isHydrated,\n isServer,\n };\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport type { FetchState } from '../types.js';\n\n/**\n * Options for useFetch hook\n */\nexport interface UseFetchOptions extends Omit<RequestInit, 'body'> {\n /** Skip initial fetch (useful for conditional fetching) */\n skip?: boolean;\n /** Dependencies that trigger refetch when changed */\n deps?: unknown[];\n /** Transform response before setting data */\n transform?: (data: unknown) => unknown;\n}\n\n/**\n * Hook for data fetching with loading and error states\n * \n * @param url URL to fetch from\n * @param options Fetch options\n * @returns Fetch state with data, loading, error, and refetch function\n * \n * @example\n * ```tsx\n * import { useFetch } from '@riktajs/react';\n * \n * interface User {\n * id: string;\n * name: string;\n * }\n * \n * function UserProfile({ userId }: { userId: string }) {\n * const { data, loading, error, refetch } = useFetch<User>(\n * `/api/users/${userId}`\n * );\n * \n * if (loading) return <Spinner />;\n * if (error) return <Error message={error} />;\n * if (!data) return null;\n * \n * return (\n * <div>\n * <h1>{data.name}</h1>\n * <button onClick={refetch}>Refresh</button>\n * </div>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // With options\n * const { data } = useFetch<Item[]>('/api/items', {\n * headers: { 'Authorization': `Bearer ${token}` },\n * deps: [token], // Refetch when token changes\n * skip: !token, // Don't fetch until we have a token\n * });\n * ```\n * \n * @example\n * ```tsx\n * // With transform\n * const { data } = useFetch<{ results: Item[] }>('/api/search', {\n * transform: (res) => res.results, // Extract just the results array\n * });\n * ```\n */\nexport function useFetch<T = unknown>(\n url: string,\n options: UseFetchOptions = {}\n): FetchState<T> {\n const { skip = false, deps = [], transform, ...fetchOptions } = options;\n \n const [state, setState] = useState<Omit<FetchState<T>, 'refetch'>>({\n data: null,\n loading: !skip,\n error: null,\n });\n\n // Use ref to track if component is mounted\n const mountedRef = useRef(true);\n // Use ref to track current fetch to handle race conditions\n const fetchIdRef = useRef(0);\n\n const doFetch = useCallback(async (ignoreSkip = false) => {\n if (skip && !ignoreSkip) return;\n\n const fetchId = ++fetchIdRef.current;\n setState(prev => ({ ...prev, loading: true, error: null }));\n\n try {\n const response = await fetch(url, fetchOptions);\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n let data = await response.json();\n\n // Apply transform if provided\n if (transform) {\n data = transform(data);\n }\n\n // Only update state if this is still the current fetch and component is mounted\n if (fetchId === fetchIdRef.current && mountedRef.current) {\n setState({ data: data as T, loading: false, error: null });\n }\n } catch (err) {\n if (fetchId === fetchIdRef.current && mountedRef.current) {\n const message = err instanceof Error ? err.message : 'An error occurred';\n setState({ data: null, loading: false, error: message });\n }\n }\n }, [url, skip, JSON.stringify(fetchOptions), transform]);\n\n // Fetch on mount and when dependencies change\n useEffect(() => {\n mountedRef.current = true;\n doFetch();\n\n return () => {\n mountedRef.current = false;\n };\n }, [doFetch, ...deps]);\n\n // refetch ignores skip option to allow manual triggering\n const refetch = useCallback(async () => {\n await doFetch(true);\n }, [doFetch]);\n\n return {\n ...state,\n refetch,\n };\n}\n","import { useState, useCallback } from 'react';\nimport type { ActionResult, ActionState } from '../types.js';\n\n/**\n * Options for useAction hook\n */\nexport interface UseActionOptions<TResult = unknown> {\n /** Callback on successful action */\n onSuccess?: (result: TResult) => void;\n /** Callback on action error */\n onError?: (error: string) => void;\n /** HTTP method to use */\n method?: 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n /** Additional headers */\n headers?: Record<string, string>;\n}\n\n/**\n * Hook for executing server actions (form submissions, mutations)\n * \n * @param url URL to send the action to\n * @param options Action options\n * @returns Action state with execute, pending, result, and reset\n * \n * @example\n * ```tsx\n * import { useAction } from '@riktajs/react';\n * \n * interface CreateItemInput {\n * name: string;\n * price: number;\n * }\n * \n * interface Item {\n * id: string;\n * name: string;\n * price: number;\n * }\n * \n * function CreateItemForm() {\n * const { execute, pending, result } = useAction<CreateItemInput, Item>(\n * '/api/items',\n * {\n * onSuccess: (item) => {\n * console.log('Created item:', item);\n * },\n * }\n * );\n * \n * const handleSubmit = async (e: FormEvent) => {\n * e.preventDefault();\n * const formData = new FormData(e.target as HTMLFormElement);\n * await execute({\n * name: formData.get('name') as string,\n * price: Number(formData.get('price')),\n * });\n * };\n * \n * return (\n * <form onSubmit={handleSubmit}>\n * <input name=\"name\" required />\n * <input name=\"price\" type=\"number\" required />\n * <button disabled={pending}>\n * {pending ? 'Creating...' : 'Create Item'}\n * </button>\n * {result?.error && <p className=\"error\">{result.error}</p>}\n * {result?.fieldErrors?.name && (\n * <p className=\"error\">{result.fieldErrors.name[0]}</p>\n * )}\n * </form>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // DELETE action\n * const { execute, pending } = useAction<{ id: string }, void>(\n * '/api/items',\n * { method: 'DELETE' }\n * );\n * \n * const handleDelete = () => execute({ id: itemId });\n * ```\n */\nexport function useAction<TInput = unknown, TResult = unknown>(\n url: string,\n options: UseActionOptions<TResult> = {}\n): ActionState<TInput, TResult> {\n const {\n onSuccess,\n onError,\n method = 'POST',\n headers: customHeaders = {},\n } = options;\n\n const [pending, setPending] = useState(false);\n const [result, setResult] = useState<ActionResult<TResult> | null>(null);\n\n const execute = useCallback(\n async (input: TInput): Promise<ActionResult<TResult>> => {\n setPending(true);\n setResult(null);\n\n try {\n const response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n ...customHeaders,\n },\n body: JSON.stringify(input),\n });\n\n const data = await response.json();\n\n if (!response.ok) {\n // Handle error response\n const actionResult: ActionResult<TResult> = {\n success: false,\n error: data.message || data.error || `HTTP ${response.status}`,\n fieldErrors: data.fieldErrors,\n };\n setResult(actionResult);\n onError?.(actionResult.error!);\n return actionResult;\n }\n\n // Handle success response\n const actionResult: ActionResult<TResult> = {\n success: true,\n data: data as TResult,\n };\n setResult(actionResult);\n onSuccess?.(data as TResult);\n return actionResult;\n } catch (err) {\n const message = err instanceof Error ? err.message : 'An error occurred';\n const actionResult: ActionResult<TResult> = {\n success: false,\n error: message,\n };\n setResult(actionResult);\n onError?.(message);\n return actionResult;\n } finally {\n setPending(false);\n }\n },\n [url, method, JSON.stringify(customHeaders), onSuccess, onError]\n );\n\n const reset = useCallback(() => {\n setResult(null);\n }, []);\n\n return {\n execute,\n pending,\n result,\n reset,\n };\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -10,6 +10,10 @@ interface SsrData<T = unknown> {
10
10
  data: T;
11
11
  /** Current URL path */
12
12
  url: string;
13
+ /** Page title (from @Ssr decorator) */
14
+ title?: string;
15
+ /** Page description (from @Ssr decorator) */
16
+ description?: string;
13
17
  /** HTTP status code */
14
18
  status?: number;
15
19
  /** Additional metadata */
@@ -26,11 +30,13 @@ interface RouterContextValue {
26
30
  /** Full URL */
27
31
  href: string;
28
32
  /** Navigate to a new URL */
29
- navigate: (url: string, options?: NavigateOptions) => void;
33
+ navigate: (url: string, options?: NavigateOptions) => void | Promise<void>;
30
34
  /** Extracted route params (e.g., { id: '123' } for /item/:id) */
31
35
  params: Record<string, string>;
32
36
  /** Update route params (used internally by RiktaProvider) */
33
37
  setParams: (params: Record<string, string>) => void;
38
+ /** Whether a navigation is in progress */
39
+ isNavigating?: boolean;
34
40
  }
35
41
  /**
36
42
  * Navigation options
@@ -141,6 +147,7 @@ declare const SsrContext: react.Context<SsrData<unknown> | null>;
141
147
  * RiktaProvider - Main provider component for Rikta React utilities
142
148
  *
143
149
  * Provides routing context, SSR data, and navigation utilities to the app.
150
+ * Automatically fetches new page data during client-side navigation.
144
151
  *
145
152
  * @example
146
153
  * ```tsx
package/dist/index.d.ts CHANGED
@@ -10,6 +10,10 @@ interface SsrData<T = unknown> {
10
10
  data: T;
11
11
  /** Current URL path */
12
12
  url: string;
13
+ /** Page title (from @Ssr decorator) */
14
+ title?: string;
15
+ /** Page description (from @Ssr decorator) */
16
+ description?: string;
13
17
  /** HTTP status code */
14
18
  status?: number;
15
19
  /** Additional metadata */
@@ -26,11 +30,13 @@ interface RouterContextValue {
26
30
  /** Full URL */
27
31
  href: string;
28
32
  /** Navigate to a new URL */
29
- navigate: (url: string, options?: NavigateOptions) => void;
33
+ navigate: (url: string, options?: NavigateOptions) => void | Promise<void>;
30
34
  /** Extracted route params (e.g., { id: '123' } for /item/:id) */
31
35
  params: Record<string, string>;
32
36
  /** Update route params (used internally by RiktaProvider) */
33
37
  setParams: (params: Record<string, string>) => void;
38
+ /** Whether a navigation is in progress */
39
+ isNavigating?: boolean;
34
40
  }
35
41
  /**
36
42
  * Navigation options
@@ -141,6 +147,7 @@ declare const SsrContext: react.Context<SsrData<unknown> | null>;
141
147
  * RiktaProvider - Main provider component for Rikta React utilities
142
148
  *
143
149
  * Provides routing context, SSR data, and navigation utilities to the app.
150
+ * Automatically fetches new page data during client-side navigation.
144
151
  *
145
152
  * @example
146
153
  * ```tsx
package/dist/index.js CHANGED
@@ -11,14 +11,26 @@ var defaultRouterContext = {
11
11
  },
12
12
  params: {},
13
13
  setParams: () => {
14
- }
14
+ },
15
+ isNavigating: false
15
16
  };
16
17
  var RouterContext = createContext(defaultRouterContext);
17
18
  RouterContext.displayName = "RiktaRouterContext";
18
19
  var SsrContext = createContext(null);
19
20
  SsrContext.displayName = "RiktaSsrContext";
20
- function getLocationInfo() {
21
+ function getLocationInfo(ssrUrl) {
21
22
  if (typeof window === "undefined") {
23
+ if (ssrUrl) {
24
+ try {
25
+ const url = new URL(ssrUrl, "http://localhost");
26
+ return {
27
+ pathname: url.pathname,
28
+ search: url.search.slice(1),
29
+ href: ssrUrl
30
+ };
31
+ } catch {
32
+ }
33
+ }
22
34
  return { pathname: "/", search: "", href: "/" };
23
35
  }
24
36
  return {
@@ -30,19 +42,42 @@ function getLocationInfo() {
30
42
  }
31
43
  function getSsrData() {
32
44
  if (typeof window === "undefined") return void 0;
33
- return window.__SSR_DATA__;
45
+ const rawData = window.__SSR_DATA__;
46
+ if (!rawData) return void 0;
47
+ return rawData;
48
+ }
49
+ async function fetchSsrData(url) {
50
+ try {
51
+ const response = await fetch(url, {
52
+ headers: {
53
+ "X-Rikta-Data": "1",
54
+ "Accept": "application/json"
55
+ }
56
+ });
57
+ if (response.ok) {
58
+ const data = await response.json();
59
+ return data;
60
+ }
61
+ } catch (error) {
62
+ console.warn("[RiktaReact] Failed to fetch SSR data:", error);
63
+ }
64
+ return null;
34
65
  }
35
66
  var RiktaProvider = ({
36
67
  ssrData: initialSsrData,
37
68
  initialParams = {},
38
69
  children
39
70
  }) => {
40
- const [ssrData] = useState(() => {
71
+ const [ssrData, setSsrData] = useState(() => {
41
72
  return initialSsrData ?? getSsrData() ?? null;
42
73
  });
43
- const [location, setLocation] = useState(getLocationInfo);
74
+ const [isNavigating, setIsNavigating] = useState(false);
75
+ const [location, setLocation] = useState(() => {
76
+ const resolvedSsrData = initialSsrData ?? getSsrData();
77
+ return getLocationInfo(resolvedSsrData?.url);
78
+ });
44
79
  const [params, setParams] = useState(initialParams);
45
- const navigate = useCallback((url, options = {}) => {
80
+ const navigate = useCallback(async (url, options = {}) => {
46
81
  const { replace = false, scroll = true, state } = options;
47
82
  if (typeof window === "undefined") return;
48
83
  let targetUrl;
@@ -56,6 +91,11 @@ var RiktaProvider = ({
56
91
  window.location.href = url;
57
92
  return;
58
93
  }
94
+ setIsNavigating(true);
95
+ const newSsrData = await fetchSsrData(targetUrl.href);
96
+ if (newSsrData) {
97
+ setSsrData(newSsrData);
98
+ }
59
99
  if (replace) {
60
100
  window.history.replaceState(state ?? null, "", targetUrl.href);
61
101
  } else {
@@ -69,12 +109,19 @@ var RiktaProvider = ({
69
109
  if (scroll) {
70
110
  window.scrollTo(0, 0);
71
111
  }
72
- window.dispatchEvent(new PopStateEvent("popstate", { state }));
112
+ setIsNavigating(false);
73
113
  }, []);
74
114
  useEffect(() => {
75
115
  if (typeof window === "undefined") return;
76
- const handlePopState = () => {
77
- setLocation(getLocationInfo());
116
+ const handlePopState = async () => {
117
+ const newLocation = getLocationInfo();
118
+ setIsNavigating(true);
119
+ const newSsrData = await fetchSsrData(newLocation.href);
120
+ if (newSsrData) {
121
+ setSsrData(newSsrData);
122
+ }
123
+ setLocation(newLocation);
124
+ setIsNavigating(false);
78
125
  };
79
126
  window.addEventListener("popstate", handlePopState);
80
127
  return () => window.removeEventListener("popstate", handlePopState);
@@ -85,8 +132,9 @@ var RiktaProvider = ({
85
132
  href: location.href,
86
133
  navigate,
87
134
  params,
88
- setParams
89
- }), [location.pathname, location.search, location.href, navigate, params]);
135
+ setParams,
136
+ isNavigating
137
+ }), [location.pathname, location.search, location.href, navigate, params, isNavigating]);
90
138
  return /* @__PURE__ */ jsx(SsrContext.Provider, { value: ssrData, children: /* @__PURE__ */ jsx(RouterContext.Provider, { value: routerValue, children }) });
91
139
  };
92
140
  RiktaProvider.displayName = "RiktaProvider";
@@ -194,8 +242,8 @@ function useFetch(url, options = {}) {
194
242
  });
195
243
  const mountedRef = useRef(true);
196
244
  const fetchIdRef = useRef(0);
197
- const fetchData = useCallback(async () => {
198
- if (skip) return;
245
+ const doFetch = useCallback(async (ignoreSkip = false) => {
246
+ if (skip && !ignoreSkip) return;
199
247
  const fetchId = ++fetchIdRef.current;
200
248
  setState((prev) => ({ ...prev, loading: true, error: null }));
201
249
  try {
@@ -219,14 +267,14 @@ function useFetch(url, options = {}) {
219
267
  }, [url, skip, JSON.stringify(fetchOptions), transform]);
220
268
  useEffect(() => {
221
269
  mountedRef.current = true;
222
- fetchData();
270
+ doFetch();
223
271
  return () => {
224
272
  mountedRef.current = false;
225
273
  };
226
- }, [fetchData, ...deps]);
274
+ }, [doFetch, ...deps]);
227
275
  const refetch = useCallback(async () => {
228
- await fetchData();
229
- }, [fetchData]);
276
+ await doFetch(true);
277
+ }, [doFetch]);
230
278
  return {
231
279
  ...state,
232
280
  refetch
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/context/RouterContext.ts","../src/context/SsrContext.ts","../src/components/RiktaProvider.tsx","../src/hooks/useNavigation.ts","../src/components/Link.tsx","../src/hooks/useParams.ts","../src/hooks/useSearchParams.ts","../src/hooks/useLocation.ts","../src/hooks/useSsrData.ts","../src/hooks/useHydration.ts","../src/hooks/useFetch.ts","../src/hooks/useAction.ts"],"names":["createContext","useCallback","jsx","useContext","useMemo","useState","useEffect","actionResult"],"mappings":"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","file":"index.js","sourcesContent":["import { createContext } from 'react';\nimport type { RouterContextValue } from '../types.js';\n\n/**\n * Default router context value for when no provider is present\n */\nconst defaultRouterContext: RouterContextValue = {\n pathname: '/',\n search: '',\n href: '/',\n navigate: () => {\n console.warn('[RiktaReact] Router context not initialized. Wrap your app with <RiktaProvider>');\n },\n params: {},\n setParams: () => {},\n};\n\n/**\n * React context for router state\n * Provides navigation utilities and current location info\n */\nexport const RouterContext = createContext<RouterContextValue>(defaultRouterContext);\n\nRouterContext.displayName = 'RiktaRouterContext';\n","import { createContext } from 'react';\nimport type { SsrData } from '../types.js';\n\n/**\n * React context for SSR data\n * Holds the server-rendered data passed via window.__SSR_DATA__\n */\nexport const SsrContext = createContext<SsrData | null>(null);\n\nSsrContext.displayName = 'RiktaSsrContext';\n","import { useState, useCallback, useEffect, useMemo, type FC } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\nimport { SsrContext } from '../context/SsrContext.js';\nimport type { RiktaProviderProps, SsrData, NavigateOptions } from '../types.js';\n\n/**\n * Get current location from window or fallback for SSR\n */\nfunction getLocationInfo() {\n if (typeof window === 'undefined') {\n return { pathname: '/', search: '', href: '/' };\n }\n return {\n pathname: window.location.pathname,\n search: window.location.search.slice(1), // Remove leading ?\n href: window.location.href,\n };\n}\n\n/**\n * Get SSR data from window\n */\nfunction getSsrData(): SsrData | undefined {\n if (typeof window === 'undefined') return undefined;\n return window.__SSR_DATA__;\n}\n\n/**\n * RiktaProvider - Main provider component for Rikta React utilities\n * \n * Provides routing context, SSR data, and navigation utilities to the app.\n * \n * @example\n * ```tsx\n * // In entry-client.tsx\n * import { RiktaProvider } from '@riktajs/react';\n * \n * hydrateRoot(\n * document.getElementById('root')!,\n * <RiktaProvider>\n * <App />\n * </RiktaProvider>\n * );\n * ```\n */\nexport const RiktaProvider: FC<RiktaProviderProps> = ({\n ssrData: initialSsrData,\n initialParams = {},\n children,\n}) => {\n // Initialize SSR data from window or props\n const [ssrData] = useState<SsrData | null>(() => {\n return initialSsrData ?? getSsrData() ?? null;\n });\n\n // Initialize location state\n const [location, setLocation] = useState(getLocationInfo);\n \n // Route params state\n const [params, setParams] = useState<Record<string, string>>(initialParams);\n\n /**\n * Navigate to a new URL using History API\n */\n const navigate = useCallback((url: string, options: NavigateOptions = {}) => {\n const { replace = false, scroll = true, state } = options;\n\n if (typeof window === 'undefined') return;\n\n // Handle full URLs vs relative paths\n let targetUrl: URL;\n try {\n targetUrl = new URL(url, window.location.origin);\n } catch {\n console.error(`[RiktaReact] Invalid URL: ${url}`);\n return;\n }\n\n // Only handle same-origin navigation\n if (targetUrl.origin !== window.location.origin) {\n window.location.href = url;\n return;\n }\n\n // Update history\n if (replace) {\n window.history.replaceState(state ?? null, '', targetUrl.href);\n } else {\n window.history.pushState(state ?? null, '', targetUrl.href);\n }\n\n // Update location state\n setLocation({\n pathname: targetUrl.pathname,\n search: targetUrl.search.slice(1),\n href: targetUrl.href,\n });\n\n // Scroll to top if requested\n if (scroll) {\n window.scrollTo(0, 0);\n }\n\n // Dispatch popstate event for any other listeners\n window.dispatchEvent(new PopStateEvent('popstate', { state }));\n }, []);\n\n // Listen for popstate (browser back/forward)\n useEffect(() => {\n if (typeof window === 'undefined') return;\n\n const handlePopState = () => {\n setLocation(getLocationInfo());\n };\n\n window.addEventListener('popstate', handlePopState);\n return () => window.removeEventListener('popstate', handlePopState);\n }, []);\n\n // Memoize router context value\n const routerValue = useMemo(() => ({\n pathname: location.pathname,\n search: location.search,\n href: location.href,\n navigate,\n params,\n setParams,\n }), [location.pathname, location.search, location.href, navigate, params]);\n\n return (\n <SsrContext.Provider value={ssrData}>\n <RouterContext.Provider value={routerValue}>\n {children}\n </RouterContext.Provider>\n </SsrContext.Provider>\n );\n};\n\nRiktaProvider.displayName = 'RiktaProvider';\n","import { useContext, useCallback } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\nimport type { NavigateOptions } from '../types.js';\n\n/**\n * Hook for programmatic navigation\n * \n * @returns Object with navigate function and current location info\n * \n * @example\n * ```tsx\n * import { useNavigation } from '@riktajs/react';\n * \n * function MyComponent() {\n * const { navigate, pathname } = useNavigation();\n * \n * const handleSubmit = async () => {\n * await saveData();\n * navigate('/success');\n * };\n * \n * return (\n * <button onClick={handleSubmit}>\n * Submit (current path: {pathname})\n * </button>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // With options\n * const { navigate } = useNavigation();\n * \n * // Replace history entry (for redirects)\n * navigate('/login', { replace: true });\n * \n * // Don't scroll to top\n * navigate('/next', { scroll: false });\n * \n * // Pass state\n * navigate('/edit', { state: { from: 'list' } });\n * ```\n */\nexport function useNavigation() {\n const context = useContext(RouterContext);\n\n const navigate = useCallback(\n (url: string, options?: NavigateOptions) => {\n context.navigate(url, options);\n },\n [context.navigate]\n );\n\n return {\n /** Navigate to a new URL */\n navigate,\n /** Current pathname */\n pathname: context.pathname,\n /** Current search string (without ?) */\n search: context.search,\n /** Full href */\n href: context.href,\n };\n}\n","import { useCallback, type FC, type MouseEvent } from 'react';\nimport { useNavigation } from '../hooks/useNavigation.js';\nimport type { LinkProps } from '../types.js';\n\n/**\n * Link component for client-side navigation\n * \n * Renders an anchor tag that uses the History API for navigation\n * instead of causing a full page reload.\n * \n * @example\n * ```tsx\n * import { Link } from '@riktajs/react';\n * \n * function Nav() {\n * return (\n * <nav>\n * <Link href=\"/\">Home</Link>\n * <Link href=\"/about\">About</Link>\n * <Link href=\"/items/123\">Item 123</Link>\n * </nav>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // With options\n * <Link href=\"/dashboard\" replace scroll={false}>\n * Dashboard\n * </Link>\n * ```\n */\nexport const Link: FC<LinkProps> = ({\n href,\n replace = false,\n scroll = true,\n prefetch = false,\n state,\n children,\n onClick,\n ...restProps\n}) => {\n const { navigate } = useNavigation();\n\n const handleClick = useCallback(\n (e: MouseEvent<HTMLAnchorElement>) => {\n // Call user's onClick handler if provided\n onClick?.(e);\n\n // Don't handle if default was prevented\n if (e.defaultPrevented) return;\n\n // Don't handle modified clicks (open in new tab, etc.)\n if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;\n\n // Don't handle right clicks\n if (e.button !== 0) return;\n\n // Don't handle target=\"_blank\" etc.\n const target = (e.currentTarget as HTMLAnchorElement).target;\n if (target && target !== '_self') return;\n\n // Don't handle external links\n try {\n const url = new URL(href, window.location.origin);\n if (url.origin !== window.location.origin) return;\n } catch {\n return;\n }\n\n // Handle the navigation\n e.preventDefault();\n navigate(href, { replace, scroll, state });\n },\n [href, replace, scroll, state, navigate, onClick]\n );\n\n // Prefetch support could be added here in the future\n // using <link rel=\"prefetch\"> or Intersection Observer\n\n return (\n <a href={href} onClick={handleClick} {...restProps}>\n {children}\n </a>\n );\n};\n\nLink.displayName = 'Link';\n","import { useContext } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\n\n/**\n * Hook to access route parameters\n * \n * Route parameters are extracted from the URL path by the server\n * and passed via SSR data. They're stored in the RouterContext.\n * \n * @returns Object with route parameter values\n * \n * @example\n * ```tsx\n * // For route /item/:id\n * import { useParams } from '@riktajs/react';\n * \n * function ItemPage() {\n * const { id } = useParams<{ id: string }>();\n * \n * return <h1>Item {id}</h1>;\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Multiple params - /users/:userId/posts/:postId\n * function PostPage() {\n * const { userId, postId } = useParams<{ userId: string; postId: string }>();\n * \n * return <h1>Post {postId} by User {userId}</h1>;\n * }\n * ```\n */\nexport function useParams<T extends Record<string, string> = Record<string, string>>(): T {\n const context = useContext(RouterContext);\n return context.params as T;\n}\n","import { useContext, useMemo } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\n\n/**\n * Hook to access and manipulate URL search parameters\n * \n * @returns Tuple of [URLSearchParams, setSearchParams function]\n * \n * @example\n * ```tsx\n * import { useSearchParams } from '@riktajs/react';\n * \n * function SearchPage() {\n * const [searchParams, setSearchParams] = useSearchParams();\n * const query = searchParams.get('q') ?? '';\n * const page = parseInt(searchParams.get('page') ?? '1', 10);\n * \n * const handleSearch = (newQuery: string) => {\n * setSearchParams({ q: newQuery, page: '1' });\n * };\n * \n * const handleNextPage = () => {\n * setSearchParams({ q: query, page: String(page + 1) });\n * };\n * \n * return (\n * <div>\n * <input \n * value={query} \n * onChange={(e) => handleSearch(e.target.value)} \n * />\n * <button onClick={handleNextPage}>Next Page</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useSearchParams(): [URLSearchParams, (params: Record<string, string> | URLSearchParams) => void] {\n const context = useContext(RouterContext);\n\n const searchParams = useMemo(() => {\n return new URLSearchParams(context.search);\n }, [context.search]);\n\n const setSearchParams = useMemo(() => {\n return (params: Record<string, string> | URLSearchParams) => {\n const newParams = params instanceof URLSearchParams \n ? params \n : new URLSearchParams(params);\n \n const search = newParams.toString();\n const newUrl = search \n ? `${context.pathname}?${search}` \n : context.pathname;\n \n context.navigate(newUrl, { scroll: false });\n };\n }, [context.pathname, context.navigate]);\n\n return [searchParams, setSearchParams];\n}\n","import { useContext } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\n\n/**\n * Location object returned by useLocation\n */\nexport interface Location {\n /** Current pathname (e.g., /items/123) */\n pathname: string;\n /** Current search string without ? (e.g., page=2&sort=asc) */\n search: string;\n /** Full href */\n href: string;\n /** Parsed search params */\n searchParams: URLSearchParams;\n}\n\n/**\n * Hook to access current location information\n * \n * @returns Location object with pathname, search, href, and searchParams\n * \n * @example\n * ```tsx\n * import { useLocation } from '@riktajs/react';\n * \n * function Breadcrumbs() {\n * const location = useLocation();\n * \n * return (\n * <nav>\n * Current path: {location.pathname}\n * {location.search && <span>?{location.search}</span>}\n * </nav>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Access search params\n * function FilterDisplay() {\n * const { searchParams } = useLocation();\n * const filter = searchParams.get('filter');\n * \n * return filter ? <span>Filtered by: {filter}</span> : null;\n * }\n * ```\n */\nexport function useLocation(): Location {\n const context = useContext(RouterContext);\n\n return {\n pathname: context.pathname,\n search: context.search,\n href: context.href,\n searchParams: new URLSearchParams(context.search),\n };\n}\n","import { useContext } from 'react';\nimport { SsrContext } from '../context/SsrContext.js';\nimport type { SsrData } from '../types.js';\n\n/**\n * Hook to access SSR data passed from server\n * \n * SSR data is passed via window.__SSR_DATA__ and contains\n * the initial data rendered on the server.\n * \n * @returns SSR data object or null if not available\n * \n * @example\n * ```tsx\n * import { useSsrData } from '@riktajs/react';\n * \n * interface PageData {\n * title: string;\n * items: Array<{ id: string; name: string }>;\n * }\n * \n * function ItemList() {\n * const ssrData = useSsrData<PageData>();\n * \n * if (!ssrData) {\n * return <div>Loading...</div>;\n * }\n * \n * return (\n * <div>\n * <h1>{ssrData.data.title}</h1>\n * <ul>\n * {ssrData.data.items.map(item => (\n * <li key={item.id}>{item.name}</li>\n * ))}\n * </ul>\n * </div>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Access just the data\n * function MyComponent() {\n * const ssrData = useSsrData<{ user: User }>();\n * const user = ssrData?.data.user;\n * \n * return user ? <UserProfile user={user} /> : <LoginPrompt />;\n * }\n * ```\n */\nexport function useSsrData<T = unknown>(): SsrData<T> | null {\n const context = useContext(SsrContext);\n return context as SsrData<T> | null;\n}\n","import { useState, useEffect } from 'react';\nimport type { HydrationState } from '../types.js';\n\n/**\n * Hook to track hydration state\n * \n * Useful for rendering different content during SSR vs after hydration,\n * or for avoiding hydration mismatches.\n * \n * @returns Hydration state object\n * \n * @example\n * ```tsx\n * import { useHydration } from '@riktajs/react';\n * \n * function TimeDisplay() {\n * const { isHydrated, isServer } = useHydration();\n * \n * // On server and initial render, show static content\n * // After hydration, show dynamic content\n * if (!isHydrated) {\n * return <span>Loading time...</span>;\n * }\n * \n * return <span>{new Date().toLocaleTimeString()}</span>;\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Avoid hydration mismatch with client-only content\n * function ClientOnlyComponent() {\n * const { isHydrated } = useHydration();\n * \n * if (!isHydrated) {\n * return null; // Or a placeholder\n * }\n * \n * return <SomeClientOnlyLibrary />;\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Conditional rendering based on environment\n * function DebugPanel() {\n * const { isServer } = useHydration();\n * \n * // Never render on server, only after client hydration\n * if (isServer) return null;\n * \n * return <DevTools />;\n * }\n * ```\n */\nexport function useHydration(): HydrationState {\n const isServer = typeof window === 'undefined';\n const [isHydrated, setIsHydrated] = useState(false);\n\n useEffect(() => {\n setIsHydrated(true);\n }, []);\n\n return {\n isHydrated,\n isServer,\n };\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport type { FetchState } from '../types.js';\n\n/**\n * Options for useFetch hook\n */\nexport interface UseFetchOptions extends Omit<RequestInit, 'body'> {\n /** Skip initial fetch (useful for conditional fetching) */\n skip?: boolean;\n /** Dependencies that trigger refetch when changed */\n deps?: unknown[];\n /** Transform response before setting data */\n transform?: (data: unknown) => unknown;\n}\n\n/**\n * Hook for data fetching with loading and error states\n * \n * @param url URL to fetch from\n * @param options Fetch options\n * @returns Fetch state with data, loading, error, and refetch function\n * \n * @example\n * ```tsx\n * import { useFetch } from '@riktajs/react';\n * \n * interface User {\n * id: string;\n * name: string;\n * }\n * \n * function UserProfile({ userId }: { userId: string }) {\n * const { data, loading, error, refetch } = useFetch<User>(\n * `/api/users/${userId}`\n * );\n * \n * if (loading) return <Spinner />;\n * if (error) return <Error message={error} />;\n * if (!data) return null;\n * \n * return (\n * <div>\n * <h1>{data.name}</h1>\n * <button onClick={refetch}>Refresh</button>\n * </div>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // With options\n * const { data } = useFetch<Item[]>('/api/items', {\n * headers: { 'Authorization': `Bearer ${token}` },\n * deps: [token], // Refetch when token changes\n * skip: !token, // Don't fetch until we have a token\n * });\n * ```\n * \n * @example\n * ```tsx\n * // With transform\n * const { data } = useFetch<{ results: Item[] }>('/api/search', {\n * transform: (res) => res.results, // Extract just the results array\n * });\n * ```\n */\nexport function useFetch<T = unknown>(\n url: string,\n options: UseFetchOptions = {}\n): FetchState<T> {\n const { skip = false, deps = [], transform, ...fetchOptions } = options;\n \n const [state, setState] = useState<Omit<FetchState<T>, 'refetch'>>({\n data: null,\n loading: !skip,\n error: null,\n });\n\n // Use ref to track if component is mounted\n const mountedRef = useRef(true);\n // Use ref to track current fetch to handle race conditions\n const fetchIdRef = useRef(0);\n\n const fetchData = useCallback(async () => {\n if (skip) return;\n\n const fetchId = ++fetchIdRef.current;\n setState(prev => ({ ...prev, loading: true, error: null }));\n\n try {\n const response = await fetch(url, fetchOptions);\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n let data = await response.json();\n\n // Apply transform if provided\n if (transform) {\n data = transform(data);\n }\n\n // Only update state if this is still the current fetch and component is mounted\n if (fetchId === fetchIdRef.current && mountedRef.current) {\n setState({ data: data as T, loading: false, error: null });\n }\n } catch (err) {\n if (fetchId === fetchIdRef.current && mountedRef.current) {\n const message = err instanceof Error ? err.message : 'An error occurred';\n setState({ data: null, loading: false, error: message });\n }\n }\n }, [url, skip, JSON.stringify(fetchOptions), transform]);\n\n // Fetch on mount and when dependencies change\n useEffect(() => {\n mountedRef.current = true;\n fetchData();\n\n return () => {\n mountedRef.current = false;\n };\n }, [fetchData, ...deps]);\n\n const refetch = useCallback(async () => {\n await fetchData();\n }, [fetchData]);\n\n return {\n ...state,\n refetch,\n };\n}\n","import { useState, useCallback } from 'react';\nimport type { ActionResult, ActionState } from '../types.js';\n\n/**\n * Options for useAction hook\n */\nexport interface UseActionOptions<TResult = unknown> {\n /** Callback on successful action */\n onSuccess?: (result: TResult) => void;\n /** Callback on action error */\n onError?: (error: string) => void;\n /** HTTP method to use */\n method?: 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n /** Additional headers */\n headers?: Record<string, string>;\n}\n\n/**\n * Hook for executing server actions (form submissions, mutations)\n * \n * @param url URL to send the action to\n * @param options Action options\n * @returns Action state with execute, pending, result, and reset\n * \n * @example\n * ```tsx\n * import { useAction } from '@riktajs/react';\n * \n * interface CreateItemInput {\n * name: string;\n * price: number;\n * }\n * \n * interface Item {\n * id: string;\n * name: string;\n * price: number;\n * }\n * \n * function CreateItemForm() {\n * const { execute, pending, result } = useAction<CreateItemInput, Item>(\n * '/api/items',\n * {\n * onSuccess: (item) => {\n * console.log('Created item:', item);\n * },\n * }\n * );\n * \n * const handleSubmit = async (e: FormEvent) => {\n * e.preventDefault();\n * const formData = new FormData(e.target as HTMLFormElement);\n * await execute({\n * name: formData.get('name') as string,\n * price: Number(formData.get('price')),\n * });\n * };\n * \n * return (\n * <form onSubmit={handleSubmit}>\n * <input name=\"name\" required />\n * <input name=\"price\" type=\"number\" required />\n * <button disabled={pending}>\n * {pending ? 'Creating...' : 'Create Item'}\n * </button>\n * {result?.error && <p className=\"error\">{result.error}</p>}\n * {result?.fieldErrors?.name && (\n * <p className=\"error\">{result.fieldErrors.name[0]}</p>\n * )}\n * </form>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // DELETE action\n * const { execute, pending } = useAction<{ id: string }, void>(\n * '/api/items',\n * { method: 'DELETE' }\n * );\n * \n * const handleDelete = () => execute({ id: itemId });\n * ```\n */\nexport function useAction<TInput = unknown, TResult = unknown>(\n url: string,\n options: UseActionOptions<TResult> = {}\n): ActionState<TInput, TResult> {\n const {\n onSuccess,\n onError,\n method = 'POST',\n headers: customHeaders = {},\n } = options;\n\n const [pending, setPending] = useState(false);\n const [result, setResult] = useState<ActionResult<TResult> | null>(null);\n\n const execute = useCallback(\n async (input: TInput): Promise<ActionResult<TResult>> => {\n setPending(true);\n setResult(null);\n\n try {\n const response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n ...customHeaders,\n },\n body: JSON.stringify(input),\n });\n\n const data = await response.json();\n\n if (!response.ok) {\n // Handle error response\n const actionResult: ActionResult<TResult> = {\n success: false,\n error: data.message || data.error || `HTTP ${response.status}`,\n fieldErrors: data.fieldErrors,\n };\n setResult(actionResult);\n onError?.(actionResult.error!);\n return actionResult;\n }\n\n // Handle success response\n const actionResult: ActionResult<TResult> = {\n success: true,\n data: data as TResult,\n };\n setResult(actionResult);\n onSuccess?.(data as TResult);\n return actionResult;\n } catch (err) {\n const message = err instanceof Error ? err.message : 'An error occurred';\n const actionResult: ActionResult<TResult> = {\n success: false,\n error: message,\n };\n setResult(actionResult);\n onError?.(message);\n return actionResult;\n } finally {\n setPending(false);\n }\n },\n [url, method, JSON.stringify(customHeaders), onSuccess, onError]\n );\n\n const reset = useCallback(() => {\n setResult(null);\n }, []);\n\n return {\n execute,\n pending,\n result,\n reset,\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/context/RouterContext.ts","../src/context/SsrContext.ts","../src/components/RiktaProvider.tsx","../src/hooks/useNavigation.ts","../src/components/Link.tsx","../src/hooks/useParams.ts","../src/hooks/useSearchParams.ts","../src/hooks/useLocation.ts","../src/hooks/useSsrData.ts","../src/hooks/useHydration.ts","../src/hooks/useFetch.ts","../src/hooks/useAction.ts"],"names":["createContext","useCallback","jsx","useContext","useMemo","useState","useEffect","actionResult"],"mappings":"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","file":"index.js","sourcesContent":["import { createContext } from 'react';\nimport type { RouterContextValue } from '../types.js';\n\n/**\n * Default router context value for when no provider is present\n */\nconst defaultRouterContext: RouterContextValue = {\n pathname: '/',\n search: '',\n href: '/',\n navigate: () => {\n console.warn('[RiktaReact] Router context not initialized. Wrap your app with <RiktaProvider>');\n },\n params: {},\n setParams: () => {},\n isNavigating: false,\n};\n\n/**\n * React context for router state\n * Provides navigation utilities and current location info\n */\nexport const RouterContext = createContext<RouterContextValue>(defaultRouterContext);\n\nRouterContext.displayName = 'RiktaRouterContext';\n","import { createContext } from 'react';\nimport type { SsrData } from '../types.js';\n\n/**\n * React context for SSR data\n * Holds the server-rendered data passed via window.__SSR_DATA__\n */\nexport const SsrContext = createContext<SsrData | null>(null);\n\nSsrContext.displayName = 'RiktaSsrContext';\n","import { useState, useCallback, useEffect, useMemo, type FC } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\nimport { SsrContext } from '../context/SsrContext.js';\nimport type { RiktaProviderProps, SsrData, NavigateOptions } from '../types.js';\n\n/**\n * Get current location from window or fallback for SSR\n */\nfunction getLocationInfo(ssrUrl?: string) {\n if (typeof window === 'undefined') {\n // During SSR, use the URL from ssrData if available\n if (ssrUrl) {\n try {\n const url = new URL(ssrUrl, 'http://localhost');\n return {\n pathname: url.pathname,\n search: url.search.slice(1),\n href: ssrUrl,\n };\n } catch {\n // Fall through to default\n }\n }\n return { pathname: '/', search: '', href: '/' };\n }\n return {\n pathname: window.location.pathname,\n search: window.location.search.slice(1), // Remove leading ?\n href: window.location.href,\n };\n}\n\n/**\n * Get SSR data from window\n * Server now puts data in normalized format: { data, url, title, description }\n */\nfunction getSsrData(): SsrData | undefined {\n if (typeof window === 'undefined') return undefined;\n \n const rawData = window.__SSR_DATA__ as Record<string, unknown> | undefined;\n if (!rawData) return undefined;\n \n // Data should already be in normalized format from entry-server\n return rawData as unknown as SsrData;\n}\n\n/**\n * Fetch SSR data from server for client-side navigation\n */\nasync function fetchSsrData(url: string): Promise<SsrData | null> {\n try {\n const response = await fetch(url, {\n headers: {\n 'X-Rikta-Data': '1',\n 'Accept': 'application/json',\n },\n });\n \n if (response.ok) {\n const data = await response.json();\n return data as SsrData;\n }\n } catch (error) {\n console.warn('[RiktaReact] Failed to fetch SSR data:', error);\n }\n return null;\n}\n\n/**\n * RiktaProvider - Main provider component for Rikta React utilities\n * \n * Provides routing context, SSR data, and navigation utilities to the app.\n * Automatically fetches new page data during client-side navigation.\n * \n * @example\n * ```tsx\n * // In entry-client.tsx\n * import { RiktaProvider } from '@riktajs/react';\n * \n * hydrateRoot(\n * document.getElementById('root')!,\n * <RiktaProvider>\n * <App />\n * </RiktaProvider>\n * );\n * ```\n */\nexport const RiktaProvider: FC<RiktaProviderProps> = ({\n ssrData: initialSsrData,\n initialParams = {},\n children,\n}) => {\n // Initialize SSR data from window or props\n const [ssrData, setSsrData] = useState<SsrData | null>(() => {\n return initialSsrData ?? getSsrData() ?? null;\n });\n\n // Loading state for navigation\n const [isNavigating, setIsNavigating] = useState(false);\n\n // Initialize location state - use URL from ssrData for SSR consistency\n const [location, setLocation] = useState(() => {\n const resolvedSsrData = initialSsrData ?? getSsrData();\n return getLocationInfo(resolvedSsrData?.url);\n });\n \n // Route params state\n const [params, setParams] = useState<Record<string, string>>(initialParams);\n\n /**\n * Navigate to a new URL using History API with data fetching\n */\n const navigate = useCallback(async (url: string, options: NavigateOptions = {}) => {\n const { replace = false, scroll = true, state } = options;\n\n if (typeof window === 'undefined') return;\n\n // Handle full URLs vs relative paths\n let targetUrl: URL;\n try {\n targetUrl = new URL(url, window.location.origin);\n } catch {\n console.error(`[RiktaReact] Invalid URL: ${url}`);\n return;\n }\n\n // Only handle same-origin navigation\n if (targetUrl.origin !== window.location.origin) {\n window.location.href = url;\n return;\n }\n\n // Start navigation\n setIsNavigating(true);\n\n // Fetch new SSR data from server BEFORE updating anything else\n const newSsrData = await fetchSsrData(targetUrl.href);\n\n // Update SSR data FIRST (before location changes)\n if (newSsrData) {\n setSsrData(newSsrData);\n }\n\n // Update history\n if (replace) {\n window.history.replaceState(state ?? null, '', targetUrl.href);\n } else {\n window.history.pushState(state ?? null, '', targetUrl.href);\n }\n\n // Update location state AFTER data is ready\n setLocation({\n pathname: targetUrl.pathname,\n search: targetUrl.search.slice(1),\n href: targetUrl.href,\n });\n\n // Scroll to top if requested\n if (scroll) {\n window.scrollTo(0, 0);\n }\n\n // End navigation\n setIsNavigating(false);\n }, []);\n\n // Listen for popstate (browser back/forward) and fetch data\n useEffect(() => {\n if (typeof window === 'undefined') return;\n\n const handlePopState = async () => {\n const newLocation = getLocationInfo();\n \n // Fetch data for the new URL FIRST\n setIsNavigating(true);\n const newSsrData = await fetchSsrData(newLocation.href);\n \n // Update SSR data BEFORE location\n if (newSsrData) {\n setSsrData(newSsrData);\n }\n \n // Then update location\n setLocation(newLocation);\n setIsNavigating(false);\n };\n\n window.addEventListener('popstate', handlePopState);\n return () => window.removeEventListener('popstate', handlePopState);\n }, []);\n\n // Memoize router context value\n const routerValue = useMemo(() => ({\n pathname: location.pathname,\n search: location.search,\n href: location.href,\n navigate,\n params,\n setParams,\n isNavigating,\n }), [location.pathname, location.search, location.href, navigate, params, isNavigating]);\n\n return (\n <SsrContext.Provider value={ssrData}>\n <RouterContext.Provider value={routerValue}>\n {children}\n </RouterContext.Provider>\n </SsrContext.Provider>\n );\n};\n\nRiktaProvider.displayName = 'RiktaProvider';\n","import { useContext, useCallback } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\nimport type { NavigateOptions } from '../types.js';\n\n/**\n * Hook for programmatic navigation\n * \n * @returns Object with navigate function and current location info\n * \n * @example\n * ```tsx\n * import { useNavigation } from '@riktajs/react';\n * \n * function MyComponent() {\n * const { navigate, pathname } = useNavigation();\n * \n * const handleSubmit = async () => {\n * await saveData();\n * navigate('/success');\n * };\n * \n * return (\n * <button onClick={handleSubmit}>\n * Submit (current path: {pathname})\n * </button>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // With options\n * const { navigate } = useNavigation();\n * \n * // Replace history entry (for redirects)\n * navigate('/login', { replace: true });\n * \n * // Don't scroll to top\n * navigate('/next', { scroll: false });\n * \n * // Pass state\n * navigate('/edit', { state: { from: 'list' } });\n * ```\n */\nexport function useNavigation() {\n const context = useContext(RouterContext);\n\n const navigate = useCallback(\n (url: string, options?: NavigateOptions) => {\n context.navigate(url, options);\n },\n [context.navigate]\n );\n\n return {\n /** Navigate to a new URL */\n navigate,\n /** Current pathname */\n pathname: context.pathname,\n /** Current search string (without ?) */\n search: context.search,\n /** Full href */\n href: context.href,\n };\n}\n","import { useCallback, type FC, type MouseEvent } from 'react';\nimport { useNavigation } from '../hooks/useNavigation.js';\nimport type { LinkProps } from '../types.js';\n\n/**\n * Link component for client-side navigation\n * \n * Renders an anchor tag that uses the History API for navigation\n * instead of causing a full page reload.\n * \n * @example\n * ```tsx\n * import { Link } from '@riktajs/react';\n * \n * function Nav() {\n * return (\n * <nav>\n * <Link href=\"/\">Home</Link>\n * <Link href=\"/about\">About</Link>\n * <Link href=\"/items/123\">Item 123</Link>\n * </nav>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // With options\n * <Link href=\"/dashboard\" replace scroll={false}>\n * Dashboard\n * </Link>\n * ```\n */\nexport const Link: FC<LinkProps> = ({\n href,\n replace = false,\n scroll = true,\n prefetch = false,\n state,\n children,\n onClick,\n ...restProps\n}) => {\n const { navigate } = useNavigation();\n\n const handleClick = useCallback(\n (e: MouseEvent<HTMLAnchorElement>) => {\n // Call user's onClick handler if provided\n onClick?.(e);\n\n // Don't handle if default was prevented\n if (e.defaultPrevented) return;\n\n // Don't handle modified clicks (open in new tab, etc.)\n if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;\n\n // Don't handle right clicks\n if (e.button !== 0) return;\n\n // Don't handle target=\"_blank\" etc.\n const target = (e.currentTarget as HTMLAnchorElement).target;\n if (target && target !== '_self') return;\n\n // Don't handle external links\n try {\n const url = new URL(href, window.location.origin);\n if (url.origin !== window.location.origin) return;\n } catch {\n return;\n }\n\n // Handle the navigation\n e.preventDefault();\n navigate(href, { replace, scroll, state });\n },\n [href, replace, scroll, state, navigate, onClick]\n );\n\n // Prefetch support could be added here in the future\n // using <link rel=\"prefetch\"> or Intersection Observer\n\n return (\n <a href={href} onClick={handleClick} {...restProps}>\n {children}\n </a>\n );\n};\n\nLink.displayName = 'Link';\n","import { useContext } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\n\n/**\n * Hook to access route parameters\n * \n * Route parameters are extracted from the URL path by the server\n * and passed via SSR data. They're stored in the RouterContext.\n * \n * @returns Object with route parameter values\n * \n * @example\n * ```tsx\n * // For route /item/:id\n * import { useParams } from '@riktajs/react';\n * \n * function ItemPage() {\n * const { id } = useParams<{ id: string }>();\n * \n * return <h1>Item {id}</h1>;\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Multiple params - /users/:userId/posts/:postId\n * function PostPage() {\n * const { userId, postId } = useParams<{ userId: string; postId: string }>();\n * \n * return <h1>Post {postId} by User {userId}</h1>;\n * }\n * ```\n */\nexport function useParams<T extends Record<string, string> = Record<string, string>>(): T {\n const context = useContext(RouterContext);\n return context.params as T;\n}\n","import { useContext, useMemo } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\n\n/**\n * Hook to access and manipulate URL search parameters\n * \n * @returns Tuple of [URLSearchParams, setSearchParams function]\n * \n * @example\n * ```tsx\n * import { useSearchParams } from '@riktajs/react';\n * \n * function SearchPage() {\n * const [searchParams, setSearchParams] = useSearchParams();\n * const query = searchParams.get('q') ?? '';\n * const page = parseInt(searchParams.get('page') ?? '1', 10);\n * \n * const handleSearch = (newQuery: string) => {\n * setSearchParams({ q: newQuery, page: '1' });\n * };\n * \n * const handleNextPage = () => {\n * setSearchParams({ q: query, page: String(page + 1) });\n * };\n * \n * return (\n * <div>\n * <input \n * value={query} \n * onChange={(e) => handleSearch(e.target.value)} \n * />\n * <button onClick={handleNextPage}>Next Page</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useSearchParams(): [URLSearchParams, (params: Record<string, string> | URLSearchParams) => void] {\n const context = useContext(RouterContext);\n\n const searchParams = useMemo(() => {\n return new URLSearchParams(context.search);\n }, [context.search]);\n\n const setSearchParams = useMemo(() => {\n return (params: Record<string, string> | URLSearchParams) => {\n const newParams = params instanceof URLSearchParams \n ? params \n : new URLSearchParams(params);\n \n const search = newParams.toString();\n const newUrl = search \n ? `${context.pathname}?${search}` \n : context.pathname;\n \n context.navigate(newUrl, { scroll: false });\n };\n }, [context.pathname, context.navigate]);\n\n return [searchParams, setSearchParams];\n}\n","import { useContext } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\n\n/**\n * Location object returned by useLocation\n */\nexport interface Location {\n /** Current pathname (e.g., /items/123) */\n pathname: string;\n /** Current search string without ? (e.g., page=2&sort=asc) */\n search: string;\n /** Full href */\n href: string;\n /** Parsed search params */\n searchParams: URLSearchParams;\n}\n\n/**\n * Hook to access current location information\n * \n * @returns Location object with pathname, search, href, and searchParams\n * \n * @example\n * ```tsx\n * import { useLocation } from '@riktajs/react';\n * \n * function Breadcrumbs() {\n * const location = useLocation();\n * \n * return (\n * <nav>\n * Current path: {location.pathname}\n * {location.search && <span>?{location.search}</span>}\n * </nav>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Access search params\n * function FilterDisplay() {\n * const { searchParams } = useLocation();\n * const filter = searchParams.get('filter');\n * \n * return filter ? <span>Filtered by: {filter}</span> : null;\n * }\n * ```\n */\nexport function useLocation(): Location {\n const context = useContext(RouterContext);\n\n return {\n pathname: context.pathname,\n search: context.search,\n href: context.href,\n searchParams: new URLSearchParams(context.search),\n };\n}\n","import { useContext } from 'react';\nimport { SsrContext } from '../context/SsrContext.js';\nimport type { SsrData } from '../types.js';\n\n/**\n * Hook to access SSR data passed from server\n * \n * SSR data is passed via window.__SSR_DATA__ and contains\n * the initial data rendered on the server.\n * \n * @returns SSR data object or null if not available\n * \n * @example\n * ```tsx\n * import { useSsrData } from '@riktajs/react';\n * \n * interface PageData {\n * title: string;\n * items: Array<{ id: string; name: string }>;\n * }\n * \n * function ItemList() {\n * const ssrData = useSsrData<PageData>();\n * \n * if (!ssrData) {\n * return <div>Loading...</div>;\n * }\n * \n * return (\n * <div>\n * <h1>{ssrData.data.title}</h1>\n * <ul>\n * {ssrData.data.items.map(item => (\n * <li key={item.id}>{item.name}</li>\n * ))}\n * </ul>\n * </div>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Access just the data\n * function MyComponent() {\n * const ssrData = useSsrData<{ user: User }>();\n * const user = ssrData?.data.user;\n * \n * return user ? <UserProfile user={user} /> : <LoginPrompt />;\n * }\n * ```\n */\nexport function useSsrData<T = unknown>(): SsrData<T> | null {\n const context = useContext(SsrContext);\n return context as SsrData<T> | null;\n}\n","import { useState, useEffect } from 'react';\nimport type { HydrationState } from '../types.js';\n\n/**\n * Hook to track hydration state\n * \n * Useful for rendering different content during SSR vs after hydration,\n * or for avoiding hydration mismatches.\n * \n * @returns Hydration state object\n * \n * @example\n * ```tsx\n * import { useHydration } from '@riktajs/react';\n * \n * function TimeDisplay() {\n * const { isHydrated, isServer } = useHydration();\n * \n * // On server and initial render, show static content\n * // After hydration, show dynamic content\n * if (!isHydrated) {\n * return <span>Loading time...</span>;\n * }\n * \n * return <span>{new Date().toLocaleTimeString()}</span>;\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Avoid hydration mismatch with client-only content\n * function ClientOnlyComponent() {\n * const { isHydrated } = useHydration();\n * \n * if (!isHydrated) {\n * return null; // Or a placeholder\n * }\n * \n * return <SomeClientOnlyLibrary />;\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Conditional rendering based on environment\n * function DebugPanel() {\n * const { isServer } = useHydration();\n * \n * // Never render on server, only after client hydration\n * if (isServer) return null;\n * \n * return <DevTools />;\n * }\n * ```\n */\nexport function useHydration(): HydrationState {\n const isServer = typeof window === 'undefined';\n const [isHydrated, setIsHydrated] = useState(false);\n\n useEffect(() => {\n setIsHydrated(true);\n }, []);\n\n return {\n isHydrated,\n isServer,\n };\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport type { FetchState } from '../types.js';\n\n/**\n * Options for useFetch hook\n */\nexport interface UseFetchOptions extends Omit<RequestInit, 'body'> {\n /** Skip initial fetch (useful for conditional fetching) */\n skip?: boolean;\n /** Dependencies that trigger refetch when changed */\n deps?: unknown[];\n /** Transform response before setting data */\n transform?: (data: unknown) => unknown;\n}\n\n/**\n * Hook for data fetching with loading and error states\n * \n * @param url URL to fetch from\n * @param options Fetch options\n * @returns Fetch state with data, loading, error, and refetch function\n * \n * @example\n * ```tsx\n * import { useFetch } from '@riktajs/react';\n * \n * interface User {\n * id: string;\n * name: string;\n * }\n * \n * function UserProfile({ userId }: { userId: string }) {\n * const { data, loading, error, refetch } = useFetch<User>(\n * `/api/users/${userId}`\n * );\n * \n * if (loading) return <Spinner />;\n * if (error) return <Error message={error} />;\n * if (!data) return null;\n * \n * return (\n * <div>\n * <h1>{data.name}</h1>\n * <button onClick={refetch}>Refresh</button>\n * </div>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // With options\n * const { data } = useFetch<Item[]>('/api/items', {\n * headers: { 'Authorization': `Bearer ${token}` },\n * deps: [token], // Refetch when token changes\n * skip: !token, // Don't fetch until we have a token\n * });\n * ```\n * \n * @example\n * ```tsx\n * // With transform\n * const { data } = useFetch<{ results: Item[] }>('/api/search', {\n * transform: (res) => res.results, // Extract just the results array\n * });\n * ```\n */\nexport function useFetch<T = unknown>(\n url: string,\n options: UseFetchOptions = {}\n): FetchState<T> {\n const { skip = false, deps = [], transform, ...fetchOptions } = options;\n \n const [state, setState] = useState<Omit<FetchState<T>, 'refetch'>>({\n data: null,\n loading: !skip,\n error: null,\n });\n\n // Use ref to track if component is mounted\n const mountedRef = useRef(true);\n // Use ref to track current fetch to handle race conditions\n const fetchIdRef = useRef(0);\n\n const doFetch = useCallback(async (ignoreSkip = false) => {\n if (skip && !ignoreSkip) return;\n\n const fetchId = ++fetchIdRef.current;\n setState(prev => ({ ...prev, loading: true, error: null }));\n\n try {\n const response = await fetch(url, fetchOptions);\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n let data = await response.json();\n\n // Apply transform if provided\n if (transform) {\n data = transform(data);\n }\n\n // Only update state if this is still the current fetch and component is mounted\n if (fetchId === fetchIdRef.current && mountedRef.current) {\n setState({ data: data as T, loading: false, error: null });\n }\n } catch (err) {\n if (fetchId === fetchIdRef.current && mountedRef.current) {\n const message = err instanceof Error ? err.message : 'An error occurred';\n setState({ data: null, loading: false, error: message });\n }\n }\n }, [url, skip, JSON.stringify(fetchOptions), transform]);\n\n // Fetch on mount and when dependencies change\n useEffect(() => {\n mountedRef.current = true;\n doFetch();\n\n return () => {\n mountedRef.current = false;\n };\n }, [doFetch, ...deps]);\n\n // refetch ignores skip option to allow manual triggering\n const refetch = useCallback(async () => {\n await doFetch(true);\n }, [doFetch]);\n\n return {\n ...state,\n refetch,\n };\n}\n","import { useState, useCallback } from 'react';\nimport type { ActionResult, ActionState } from '../types.js';\n\n/**\n * Options for useAction hook\n */\nexport interface UseActionOptions<TResult = unknown> {\n /** Callback on successful action */\n onSuccess?: (result: TResult) => void;\n /** Callback on action error */\n onError?: (error: string) => void;\n /** HTTP method to use */\n method?: 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n /** Additional headers */\n headers?: Record<string, string>;\n}\n\n/**\n * Hook for executing server actions (form submissions, mutations)\n * \n * @param url URL to send the action to\n * @param options Action options\n * @returns Action state with execute, pending, result, and reset\n * \n * @example\n * ```tsx\n * import { useAction } from '@riktajs/react';\n * \n * interface CreateItemInput {\n * name: string;\n * price: number;\n * }\n * \n * interface Item {\n * id: string;\n * name: string;\n * price: number;\n * }\n * \n * function CreateItemForm() {\n * const { execute, pending, result } = useAction<CreateItemInput, Item>(\n * '/api/items',\n * {\n * onSuccess: (item) => {\n * console.log('Created item:', item);\n * },\n * }\n * );\n * \n * const handleSubmit = async (e: FormEvent) => {\n * e.preventDefault();\n * const formData = new FormData(e.target as HTMLFormElement);\n * await execute({\n * name: formData.get('name') as string,\n * price: Number(formData.get('price')),\n * });\n * };\n * \n * return (\n * <form onSubmit={handleSubmit}>\n * <input name=\"name\" required />\n * <input name=\"price\" type=\"number\" required />\n * <button disabled={pending}>\n * {pending ? 'Creating...' : 'Create Item'}\n * </button>\n * {result?.error && <p className=\"error\">{result.error}</p>}\n * {result?.fieldErrors?.name && (\n * <p className=\"error\">{result.fieldErrors.name[0]}</p>\n * )}\n * </form>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // DELETE action\n * const { execute, pending } = useAction<{ id: string }, void>(\n * '/api/items',\n * { method: 'DELETE' }\n * );\n * \n * const handleDelete = () => execute({ id: itemId });\n * ```\n */\nexport function useAction<TInput = unknown, TResult = unknown>(\n url: string,\n options: UseActionOptions<TResult> = {}\n): ActionState<TInput, TResult> {\n const {\n onSuccess,\n onError,\n method = 'POST',\n headers: customHeaders = {},\n } = options;\n\n const [pending, setPending] = useState(false);\n const [result, setResult] = useState<ActionResult<TResult> | null>(null);\n\n const execute = useCallback(\n async (input: TInput): Promise<ActionResult<TResult>> => {\n setPending(true);\n setResult(null);\n\n try {\n const response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n ...customHeaders,\n },\n body: JSON.stringify(input),\n });\n\n const data = await response.json();\n\n if (!response.ok) {\n // Handle error response\n const actionResult: ActionResult<TResult> = {\n success: false,\n error: data.message || data.error || `HTTP ${response.status}`,\n fieldErrors: data.fieldErrors,\n };\n setResult(actionResult);\n onError?.(actionResult.error!);\n return actionResult;\n }\n\n // Handle success response\n const actionResult: ActionResult<TResult> = {\n success: true,\n data: data as TResult,\n };\n setResult(actionResult);\n onSuccess?.(data as TResult);\n return actionResult;\n } catch (err) {\n const message = err instanceof Error ? err.message : 'An error occurred';\n const actionResult: ActionResult<TResult> = {\n success: false,\n error: message,\n };\n setResult(actionResult);\n onError?.(message);\n return actionResult;\n } finally {\n setPending(false);\n }\n },\n [url, method, JSON.stringify(customHeaders), onSuccess, onError]\n );\n\n const reset = useCallback(() => {\n setResult(null);\n }, []);\n\n return {\n execute,\n pending,\n result,\n reset,\n };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riktajs/react",
3
- "version": "0.10.3",
3
+ "version": "0.11.4",
4
4
  "description": "React utilities for Rikta SSR framework - hooks and components for routing, data fetching, and server interaction",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",