@flexireact/core 3.0.4 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,22 +10,19 @@ function hydrateIsland(islandId, Component, props) {
10
10
  if (element.hasAttribute("data-hydrated")) {
11
11
  return;
12
12
  }
13
- try {
14
- hydrateRoot(element, React.createElement(Component, props));
15
- element.setAttribute("data-hydrated", "true");
16
- element.dispatchEvent(new CustomEvent("flexi:hydrated", {
17
- bubbles: true,
18
- detail: { islandId, props }
19
- }));
20
- } catch (error) {
21
- console.error(`Failed to hydrate island ${islandId}:`, error);
22
- try {
23
- createRoot(element).render(React.createElement(Component, props));
24
- element.setAttribute("data-hydrated", "true");
25
- } catch (fallbackError) {
26
- console.error(`Fallback render also failed for ${islandId}:`, fallbackError);
13
+ hydrateRoot(element, React.createElement(Component, props), {
14
+ onRecoverableError: (error, errorInfo) => {
15
+ console.warn(`[FlexiReact] Hydration mismatch in ${islandId}:`, error);
16
+ if (process.env.NODE_ENV === "development") {
17
+ console.debug("Component stack:", errorInfo.componentStack);
18
+ }
27
19
  }
28
- }
20
+ });
21
+ element.setAttribute("data-hydrated", "true");
22
+ element.dispatchEvent(new CustomEvent("flexi:hydrated", {
23
+ bubbles: true,
24
+ detail: { islandId, props }
25
+ }));
29
26
  }
30
27
  function hydrateApp(App, props = {}) {
31
28
  const root = document.getElementById("root");
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../core/client/hydration.ts","../../../core/client/navigation.ts","../../../core/client/islands.ts","../../../core/client/Link.tsx"],"sourcesContent":["/**\r\n * FlexiReact Client Hydration\r\n * Handles selective hydration of islands and full app hydration\r\n */\r\n\r\nimport React from 'react';\r\nimport { hydrateRoot, createRoot } from 'react-dom/client';\r\n\r\n// Extend Window interface for __FLEXI_DATA__\r\ndeclare global {\r\n interface Window {\r\n __FLEXI_DATA__?: {\r\n islands?: any[];\r\n props?: Record<string, any>;\r\n };\r\n }\r\n}\r\n\r\n/**\r\n * Hydrates a specific island component\r\n */\r\nexport function hydrateIsland(islandId, Component, props) {\r\n const element = document.querySelector(`[data-island=\"${islandId}\"]`);\r\n \r\n if (!element) {\r\n console.warn(`Island element not found: ${islandId}`);\r\n return;\r\n }\r\n\r\n if (element.hasAttribute('data-hydrated')) {\r\n return; // Already hydrated\r\n }\r\n\r\n try {\r\n hydrateRoot(element, React.createElement(Component, props));\r\n element.setAttribute('data-hydrated', 'true');\r\n \r\n // Dispatch custom event\r\n element.dispatchEvent(new CustomEvent('flexi:hydrated', {\r\n bubbles: true,\r\n detail: { islandId, props }\r\n }));\r\n } catch (error) {\r\n console.error(`Failed to hydrate island ${islandId}:`, error);\r\n \r\n // Fallback: try full render instead of hydration\r\n try {\r\n createRoot(element).render(React.createElement(Component, props));\r\n element.setAttribute('data-hydrated', 'true');\r\n } catch (fallbackError) {\r\n console.error(`Fallback render also failed for ${islandId}:`, fallbackError);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Hydrates the entire application\r\n */\r\nexport function hydrateApp(App, props = {}) {\r\n const root = document.getElementById('root');\r\n \r\n if (!root) {\r\n console.error('Root element not found');\r\n return;\r\n }\r\n\r\n // Get server-rendered props\r\n const serverProps = window.__FLEXI_DATA__?.props || {};\r\n const mergedProps = { ...serverProps, ...props };\r\n\r\n try {\r\n hydrateRoot(root, React.createElement(App, mergedProps));\r\n } catch (error) {\r\n console.error('Hydration failed, falling back to full render:', error);\r\n createRoot(root).render(React.createElement(App, mergedProps));\r\n }\r\n}\r\n\r\n/**\r\n * Hydrates all islands on the page\r\n */\r\nexport async function hydrateAllIslands(islandModules) {\r\n const islands = document.querySelectorAll('[data-island]');\r\n \r\n for (const element of islands) {\r\n if (element.hasAttribute('data-hydrated')) continue;\r\n \r\n const islandId = element.getAttribute('data-island');\r\n const islandName = element.getAttribute('data-island-name');\r\n const propsJson = element.getAttribute('data-island-props');\r\n \r\n try {\r\n const props = propsJson ? JSON.parse(propsJson) : {};\r\n const module = islandModules[islandName];\r\n \r\n if (module) {\r\n hydrateIsland(islandId, module.default || module, props);\r\n }\r\n } catch (error) {\r\n console.error(`Failed to hydrate island ${islandName}:`, error);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Progressive hydration based on visibility\r\n */\r\nexport function setupProgressiveHydration(islandModules) {\r\n const observer = new IntersectionObserver(\r\n (entries) => {\r\n entries.forEach(async (entry) => {\r\n if (!entry.isIntersecting) return;\r\n \r\n const element = entry.target;\r\n if (element.hasAttribute('data-hydrated')) return;\r\n \r\n const islandName = element.getAttribute('data-island-name');\r\n const islandId = element.getAttribute('data-island');\r\n const propsJson = element.getAttribute('data-island-props');\r\n \r\n try {\r\n const props = propsJson ? JSON.parse(propsJson) : {};\r\n const module = await islandModules[islandName]();\r\n \r\n hydrateIsland(islandId, module.default || module, props);\r\n observer.unobserve(element);\r\n } catch (error) {\r\n console.error(`Failed to hydrate island ${islandName}:`, error);\r\n }\r\n });\r\n },\r\n { rootMargin: '50px' }\r\n );\r\n\r\n document.querySelectorAll('[data-island]:not([data-hydrated])').forEach(el => {\r\n observer.observe(el);\r\n });\r\n\r\n return observer;\r\n}\r\n\r\nexport default {\r\n hydrateIsland,\r\n hydrateApp,\r\n hydrateAllIslands,\r\n setupProgressiveHydration\r\n};\r\n","/**\r\n * FlexiReact Client Navigation\r\n * Client-side navigation with prefetching\r\n */\r\n\r\nimport React from 'react';\r\n\r\n// Navigation state\r\nconst navigationState: {\r\n listeners: Set<(url: string) => void>;\r\n prefetched: Set<string>;\r\n} = {\r\n listeners: new Set(),\r\n prefetched: new Set()\r\n};\r\n\r\n/**\r\n * Navigates to a new URL\r\n */\r\ninterface NavigateOptions {\r\n replace?: boolean;\r\n scroll?: boolean;\r\n}\r\n\r\nexport function navigate(url: string, options: NavigateOptions = {}) {\r\n const { replace = false, scroll = true } = options;\r\n\r\n if (replace) {\r\n window.history.replaceState({}, '', url);\r\n } else {\r\n window.history.pushState({}, '', url);\r\n }\r\n\r\n // Dispatch navigation event\r\n window.dispatchEvent(new CustomEvent('flexi:navigate', {\r\n detail: { url, replace, scroll }\r\n }));\r\n\r\n // Scroll to top if needed\r\n if (scroll) {\r\n window.scrollTo(0, 0);\r\n }\r\n\r\n // Notify listeners\r\n navigationState.listeners.forEach(listener => listener(url));\r\n\r\n // Fetch and render new page\r\n return fetchAndRender(url);\r\n}\r\n\r\n/**\r\n * Prefetches a URL for faster navigation\r\n */\r\nexport function prefetch(url) {\r\n if (navigationState.prefetched.has(url)) {\r\n return Promise.resolve();\r\n }\r\n\r\n navigationState.prefetched.add(url);\r\n\r\n // Create a link element for prefetching\r\n const link = document.createElement('link');\r\n link.rel = 'prefetch';\r\n link.href = url;\r\n document.head.appendChild(link);\r\n\r\n return Promise.resolve();\r\n}\r\n\r\n/**\r\n * Fetches and renders a new page\r\n */\r\nasync function fetchAndRender(url) {\r\n try {\r\n const response = await fetch(url, {\r\n headers: {\r\n 'X-Flexi-Navigation': 'true'\r\n }\r\n });\r\n\r\n if (!response.ok) {\r\n throw new Error(`Navigation failed: ${response.status}`);\r\n }\r\n\r\n const html = await response.text();\r\n \r\n // Parse the HTML\r\n const parser = new DOMParser();\r\n const doc = parser.parseFromString(html, 'text/html');\r\n\r\n // Update the page content\r\n const newRoot = doc.getElementById('root');\r\n const currentRoot = document.getElementById('root');\r\n\r\n if (newRoot && currentRoot) {\r\n currentRoot.innerHTML = newRoot.innerHTML;\r\n }\r\n\r\n // Update the title\r\n document.title = doc.title;\r\n\r\n // Update meta tags\r\n updateMetaTags(doc);\r\n\r\n // Re-hydrate islands\r\n window.dispatchEvent(new CustomEvent('flexi:pageload'));\r\n\r\n } catch (error) {\r\n console.error('Navigation error:', error);\r\n // Fallback to full page navigation\r\n window.location.href = url;\r\n }\r\n}\r\n\r\n/**\r\n * Updates meta tags from new document\r\n */\r\nfunction updateMetaTags(doc) {\r\n // Remove old meta tags\r\n document.querySelectorAll('meta[data-flexi]').forEach(el => el.remove());\r\n\r\n // Add new meta tags\r\n doc.querySelectorAll('meta').forEach(meta => {\r\n if (meta.name || meta.property) {\r\n const newMeta = meta.cloneNode(true);\r\n newMeta.setAttribute('data-flexi', 'true');\r\n document.head.appendChild(newMeta);\r\n }\r\n });\r\n}\r\n\r\n/**\r\n * Link component for client-side navigation\r\n */\r\nexport function Link({ href, children, prefetch: shouldPrefetch = true, replace = false, className, ...props }) {\r\n const handleClick = (e) => {\r\n // Allow normal navigation for external links or modified clicks\r\n if (\r\n e.ctrlKey ||\r\n e.metaKey ||\r\n e.shiftKey ||\r\n e.button !== 0 ||\r\n href.startsWith('http') ||\r\n href.startsWith('//')\r\n ) {\r\n return;\r\n }\r\n\r\n e.preventDefault();\r\n navigate(href, { replace });\r\n };\r\n\r\n const handleMouseEnter = () => {\r\n if (shouldPrefetch) {\r\n prefetch(href);\r\n }\r\n };\r\n\r\n return React.createElement('a', {\r\n href,\r\n onClick: handleClick,\r\n onMouseEnter: handleMouseEnter,\r\n className,\r\n ...props\r\n }, children);\r\n}\r\n\r\n/**\r\n * Hook to listen for navigation events\r\n */\r\nexport function useNavigation() {\r\n const [pathname, setPathname] = React.useState(\r\n typeof window !== 'undefined' ? window.location.pathname : '/'\r\n );\r\n\r\n React.useEffect(() => {\r\n const handleNavigation = (url) => {\r\n setPathname(new URL(url, window.location.origin).pathname);\r\n };\r\n\r\n navigationState.listeners.add(handleNavigation);\r\n\r\n const handlePopState = () => {\r\n setPathname(window.location.pathname);\r\n navigationState.listeners.forEach(listener => listener(window.location.pathname));\r\n };\r\n\r\n window.addEventListener('popstate', handlePopState);\r\n\r\n return () => {\r\n navigationState.listeners.delete(handleNavigation);\r\n window.removeEventListener('popstate', handlePopState);\r\n };\r\n }, []);\r\n\r\n return { pathname, navigate, prefetch };\r\n}\r\n\r\n// Setup popstate listener\r\nif (typeof window !== 'undefined') {\r\n window.addEventListener('popstate', () => {\r\n const url = window.location.pathname + window.location.search;\r\n navigationState.listeners.forEach(listener => listener(url));\r\n });\r\n}\r\n\r\nexport default {\r\n navigate,\r\n prefetch,\r\n Link,\r\n useNavigation\r\n};\r\n","/**\r\n * FlexiReact Client Islands\r\n * Client-side island utilities\r\n */\r\n\r\nimport React from 'react';\r\n\r\n/**\r\n * Hook to check if component is hydrated\r\n */\r\nexport function useIsland() {\r\n const [isHydrated, setIsHydrated] = React.useState(false);\r\n\r\n React.useEffect(() => {\r\n setIsHydrated(true);\r\n }, []);\r\n\r\n return { isHydrated };\r\n}\r\n\r\n/**\r\n * Island boundary component\r\n * Wraps interactive components for partial hydration\r\n */\r\nexport function IslandBoundary({ children, fallback = null, name = 'island' }) {\r\n const { isHydrated } = useIsland();\r\n\r\n // On server or before hydration, render children normally\r\n // The server will wrap this in the island marker\r\n if (!isHydrated && fallback) {\r\n return fallback;\r\n }\r\n\r\n return React.createElement('div', {\r\n 'data-island-boundary': name,\r\n children\r\n });\r\n}\r\n\r\n/**\r\n * Creates a lazy-loaded island\r\n */\r\ninterface LazyIslandOptions {\r\n fallback?: React.ReactNode;\r\n name?: string;\r\n}\r\n\r\nexport function createClientIsland(loader: () => Promise<any>, options: LazyIslandOptions = {}) {\r\n const { fallback = null, name = 'lazy-island' } = options;\r\n\r\n return function LazyIsland(props) {\r\n const [Component, setComponent] = React.useState(null);\r\n const [error, setError] = React.useState(null);\r\n\r\n React.useEffect(() => {\r\n loader()\r\n .then(mod => setComponent(() => mod.default || mod))\r\n .catch(err => setError(err));\r\n }, []);\r\n\r\n if (error) {\r\n return React.createElement('div', {\r\n className: 'island-error',\r\n children: `Failed to load ${name}`\r\n });\r\n }\r\n\r\n if (!Component) {\r\n return fallback || React.createElement('div', {\r\n className: 'island-loading',\r\n children: 'Loading...'\r\n });\r\n }\r\n\r\n return React.createElement(Component, props);\r\n };\r\n}\r\n\r\n/**\r\n * Island with interaction trigger\r\n * Only hydrates when user interacts\r\n */\r\nexport function InteractiveIsland({ children, trigger = 'click', fallback }) {\r\n const [shouldHydrate, setShouldHydrate] = React.useState(false);\r\n const ref = React.useRef(null);\r\n\r\n React.useEffect(() => {\r\n const element = ref.current;\r\n if (!element) return;\r\n\r\n const handleInteraction = () => {\r\n setShouldHydrate(true);\r\n };\r\n\r\n element.addEventListener(trigger, handleInteraction, { once: true });\r\n\r\n return () => {\r\n element.removeEventListener(trigger, handleInteraction);\r\n };\r\n }, [trigger]);\r\n\r\n if (!shouldHydrate) {\r\n return React.createElement('div', {\r\n ref,\r\n 'data-interactive-island': 'true',\r\n children: fallback || children\r\n });\r\n }\r\n\r\n return children;\r\n}\r\n\r\n/**\r\n * Media query island\r\n * Only hydrates when media query matches\r\n */\r\nexport function MediaIsland({ children, query, fallback }) {\r\n const [matches, setMatches] = React.useState(false);\r\n\r\n React.useEffect(() => {\r\n const mediaQuery = window.matchMedia(query);\r\n setMatches(mediaQuery.matches);\r\n\r\n const handler = (e) => setMatches(e.matches);\r\n mediaQuery.addEventListener('change', handler);\r\n\r\n return () => mediaQuery.removeEventListener('change', handler);\r\n }, [query]);\r\n\r\n if (!matches) {\r\n return fallback || null;\r\n }\r\n\r\n return children;\r\n}\r\n\r\nexport default {\r\n useIsland,\r\n IslandBoundary,\r\n createClientIsland,\r\n InteractiveIsland,\r\n MediaIsland\r\n};\r\n","'use client';\r\n\r\n/**\r\n * FlexiReact Link Component\r\n * Enhanced link with prefetching, client-side navigation, and loading states\r\n */\r\n\r\nimport React, { useCallback, useEffect, useRef, useState } from 'react';\r\n\r\nexport interface LinkProps extends Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> {\r\n /** The URL to navigate to */\r\n href: string;\r\n /** Prefetch the page on hover/visibility */\r\n prefetch?: boolean | 'hover' | 'viewport';\r\n /** Replace the current history entry instead of pushing */\r\n replace?: boolean;\r\n /** Scroll to top after navigation */\r\n scroll?: boolean;\r\n /** Show loading indicator while navigating */\r\n showLoading?: boolean;\r\n /** Custom loading component */\r\n loadingComponent?: React.ReactNode;\r\n /** Callback when navigation starts */\r\n onNavigationStart?: () => void;\r\n /** Callback when navigation ends */\r\n onNavigationEnd?: () => void;\r\n /** Children */\r\n children: React.ReactNode;\r\n}\r\n\r\n// Prefetch cache to avoid duplicate requests\r\nconst prefetchCache = new Set<string>();\r\n\r\n// Prefetch a URL\r\nasync function prefetchUrl(url: string): Promise<void> {\r\n if (prefetchCache.has(url)) return;\r\n \r\n try {\r\n // Mark as prefetched immediately to prevent duplicate requests\r\n prefetchCache.add(url);\r\n \r\n // Use link preload for better browser optimization\r\n const link = document.createElement('link');\r\n link.rel = 'prefetch';\r\n link.href = url;\r\n link.as = 'document';\r\n document.head.appendChild(link);\r\n \r\n // Also fetch the page to warm the cache\r\n const controller = new AbortController();\r\n const timeoutId = setTimeout(() => controller.abort(), 5000);\r\n \r\n await fetch(url, {\r\n method: 'GET',\r\n credentials: 'same-origin',\r\n signal: controller.signal,\r\n headers: {\r\n 'X-Flexi-Prefetch': '1',\r\n 'Accept': 'text/html'\r\n }\r\n });\r\n \r\n clearTimeout(timeoutId);\r\n } catch (error) {\r\n // Remove from cache on error so it can be retried\r\n prefetchCache.delete(url);\r\n }\r\n}\r\n\r\n// Check if URL is internal\r\nfunction isInternalUrl(url: string): boolean {\r\n if (url.startsWith('/')) return true;\r\n if (url.startsWith('#')) return true;\r\n \r\n try {\r\n const parsed = new URL(url, window.location.origin);\r\n return parsed.origin === window.location.origin;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n// Navigate to a URL\r\nfunction navigate(url: string, options: { replace?: boolean; scroll?: boolean } = {}): void {\r\n const { replace = false, scroll = true } = options;\r\n \r\n if (replace) {\r\n window.history.replaceState({}, '', url);\r\n } else {\r\n window.history.pushState({}, '', url);\r\n }\r\n \r\n // Dispatch popstate event to trigger any listeners\r\n window.dispatchEvent(new PopStateEvent('popstate', { state: {} }));\r\n \r\n // Scroll to top if requested\r\n if (scroll) {\r\n window.scrollTo({ top: 0, behavior: 'smooth' });\r\n }\r\n}\r\n\r\n/**\r\n * Link component with prefetching and client-side navigation\r\n * \r\n * @example\r\n * ```tsx\r\n * import { Link } from '@flexireact/core/client';\r\n * \r\n * // Basic usage\r\n * <Link href=\"/about\">About</Link>\r\n * \r\n * // With prefetch on hover\r\n * <Link href=\"/products\" prefetch=\"hover\">Products</Link>\r\n * \r\n * // With prefetch on viewport visibility\r\n * <Link href=\"/contact\" prefetch=\"viewport\">Contact</Link>\r\n * \r\n * // Replace history instead of push\r\n * <Link href=\"/login\" replace>Login</Link>\r\n * \r\n * // Disable scroll to top\r\n * <Link href=\"/section#anchor\" scroll={false}>Go to section</Link>\r\n * ```\r\n */\r\nexport function Link({\r\n href,\r\n prefetch = true,\r\n replace = false,\r\n scroll = true,\r\n showLoading = false,\r\n loadingComponent,\r\n onNavigationStart,\r\n onNavigationEnd,\r\n children,\r\n className,\r\n onClick,\r\n onMouseEnter,\r\n onFocus,\r\n ...props\r\n}: LinkProps) {\r\n const [isNavigating, setIsNavigating] = useState(false);\r\n const linkRef = useRef<HTMLAnchorElement>(null);\r\n const hasPrefetched = useRef(false);\r\n\r\n // Prefetch on viewport visibility\r\n useEffect(() => {\r\n if (prefetch !== 'viewport' && prefetch !== true) return;\r\n if (!isInternalUrl(href)) return;\r\n if (hasPrefetched.current) return;\r\n\r\n const observer = new IntersectionObserver(\r\n (entries) => {\r\n entries.forEach((entry) => {\r\n if (entry.isIntersecting) {\r\n prefetchUrl(href);\r\n hasPrefetched.current = true;\r\n observer.disconnect();\r\n }\r\n });\r\n },\r\n { rootMargin: '200px' }\r\n );\r\n\r\n if (linkRef.current) {\r\n observer.observe(linkRef.current);\r\n }\r\n\r\n return () => observer.disconnect();\r\n }, [href, prefetch]);\r\n\r\n // Handle hover prefetch\r\n const handleMouseEnter = useCallback(\r\n (e: React.MouseEvent<HTMLAnchorElement>) => {\r\n onMouseEnter?.(e);\r\n \r\n if ((prefetch === 'hover' || prefetch === true) && isInternalUrl(href)) {\r\n prefetchUrl(href);\r\n }\r\n },\r\n [href, prefetch, onMouseEnter]\r\n );\r\n\r\n // Handle focus prefetch (for keyboard navigation)\r\n const handleFocus = useCallback(\r\n (e: React.FocusEvent<HTMLAnchorElement>) => {\r\n onFocus?.(e);\r\n \r\n if ((prefetch === 'hover' || prefetch === true) && isInternalUrl(href)) {\r\n prefetchUrl(href);\r\n }\r\n },\r\n [href, prefetch, onFocus]\r\n );\r\n\r\n // Handle click for client-side navigation\r\n const handleClick = useCallback(\r\n async (e: React.MouseEvent<HTMLAnchorElement>) => {\r\n onClick?.(e);\r\n \r\n // Don't handle if default was prevented\r\n if (e.defaultPrevented) return;\r\n \r\n // Don't handle if modifier keys are pressed (open in new tab, etc.)\r\n if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;\r\n \r\n // Don't handle external URLs\r\n if (!isInternalUrl(href)) return;\r\n \r\n // Don't handle if target is set\r\n if (props.target && props.target !== '_self') return;\r\n \r\n // Prevent default navigation\r\n e.preventDefault();\r\n \r\n // Start navigation\r\n setIsNavigating(true);\r\n onNavigationStart?.();\r\n \r\n try {\r\n // Fetch the new page\r\n const response = await fetch(href, {\r\n method: 'GET',\r\n credentials: 'same-origin',\r\n headers: {\r\n 'X-Flexi-Navigation': '1',\r\n 'Accept': 'text/html'\r\n }\r\n });\r\n \r\n if (response.ok) {\r\n const html = await response.text();\r\n \r\n // Parse and update the page\r\n const parser = new DOMParser();\r\n const doc = parser.parseFromString(html, 'text/html');\r\n \r\n // Update title\r\n const newTitle = doc.querySelector('title')?.textContent;\r\n if (newTitle) {\r\n document.title = newTitle;\r\n }\r\n \r\n // Update body content (or specific container)\r\n const newContent = doc.querySelector('#root') || doc.body;\r\n const currentContent = document.querySelector('#root') || document.body;\r\n \r\n if (newContent && currentContent) {\r\n currentContent.innerHTML = newContent.innerHTML;\r\n }\r\n \r\n // Update URL\r\n navigate(href, { replace, scroll });\r\n } else {\r\n // Fallback to regular navigation on error\r\n window.location.href = href;\r\n }\r\n } catch (error) {\r\n // Fallback to regular navigation on error\r\n window.location.href = href;\r\n } finally {\r\n setIsNavigating(false);\r\n onNavigationEnd?.();\r\n }\r\n },\r\n [href, replace, scroll, onClick, onNavigationStart, onNavigationEnd, props.target]\r\n );\r\n\r\n return (\r\n <a\r\n ref={linkRef}\r\n href={href}\r\n className={className}\r\n onClick={handleClick}\r\n onMouseEnter={handleMouseEnter}\r\n onFocus={handleFocus}\r\n data-prefetch={prefetch}\r\n data-navigating={isNavigating || undefined}\r\n {...props}\r\n >\r\n {showLoading && isNavigating ? (\r\n loadingComponent || (\r\n <span className=\"flexi-link-loading\">\r\n <span className=\"flexi-link-spinner\" />\r\n {children}\r\n </span>\r\n )\r\n ) : (\r\n children\r\n )}\r\n </a>\r\n );\r\n}\r\n\r\n/**\r\n * Programmatic navigation function\r\n * \r\n * @example\r\n * ```tsx\r\n * import { useRouter } from '@flexireact/core/client';\r\n * \r\n * function MyComponent() {\r\n * const router = useRouter();\r\n * \r\n * const handleClick = () => {\r\n * router.push('/dashboard');\r\n * };\r\n * \r\n * return <button onClick={handleClick}>Go to Dashboard</button>;\r\n * }\r\n * ```\r\n */\r\nexport function useRouter() {\r\n return {\r\n push(url: string, options?: { scroll?: boolean }) {\r\n navigate(url, { replace: false, scroll: options?.scroll ?? true });\r\n // Trigger page reload for now (full SPA navigation requires more work)\r\n window.location.href = url;\r\n },\r\n \r\n replace(url: string, options?: { scroll?: boolean }) {\r\n navigate(url, { replace: true, scroll: options?.scroll ?? true });\r\n window.location.href = url;\r\n },\r\n \r\n back() {\r\n window.history.back();\r\n },\r\n \r\n forward() {\r\n window.history.forward();\r\n },\r\n \r\n prefetch(url: string) {\r\n if (isInternalUrl(url)) {\r\n prefetchUrl(url);\r\n }\r\n },\r\n \r\n refresh() {\r\n window.location.reload();\r\n }\r\n };\r\n}\r\n\r\nexport default Link;\r\n"],"mappings":"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","names":["React","React","navigate","Link","prefetch"]}
1
+ {"version":3,"sources":["../../../core/client/hydration.ts","../../../core/client/navigation.ts","../../../core/client/islands.ts","../../../core/client/Link.tsx"],"sourcesContent":["/**\r\n * FlexiReact Client Hydration\r\n * Handles selective hydration of islands and full app hydration\r\n */\r\n\r\nimport React from 'react';\r\nimport { hydrateRoot, createRoot } from 'react-dom/client';\r\n\r\n// Extend Window interface for __FLEXI_DATA__\r\ndeclare global {\r\n interface Window {\r\n __FLEXI_DATA__?: {\r\n islands?: any[];\r\n props?: Record<string, any>;\r\n };\r\n }\r\n}\r\n\r\n/**\r\n * Hydrates a specific island component\r\n * React 19: Uses built-in hydration error recovery\r\n */\r\nexport function hydrateIsland(islandId, Component, props) {\r\n const element = document.querySelector(`[data-island=\"${islandId}\"]`);\r\n\r\n if (!element) {\r\n console.warn(`Island element not found: ${islandId}`);\r\n return;\r\n }\r\n\r\n if (element.hasAttribute('data-hydrated')) {\r\n return; // Already hydrated\r\n }\r\n\r\n // React 19: Built-in hydration error recovery\r\n hydrateRoot(element, React.createElement(Component, props), {\r\n onRecoverableError: (error, errorInfo) => {\r\n console.warn(`[FlexiReact] Hydration mismatch in ${islandId}:`, error);\r\n if (process.env.NODE_ENV === 'development') {\r\n console.debug('Component stack:', errorInfo.componentStack);\r\n }\r\n }\r\n });\r\n\r\n element.setAttribute('data-hydrated', 'true');\r\n\r\n // Dispatch custom event\r\n element.dispatchEvent(new CustomEvent('flexi:hydrated', {\r\n bubbles: true,\r\n detail: { islandId, props }\r\n }));\r\n}\r\n\r\n/**\r\n * Hydrates the entire application\r\n */\r\nexport function hydrateApp(App, props = {}) {\r\n const root = document.getElementById('root');\r\n\r\n if (!root) {\r\n console.error('Root element not found');\r\n return;\r\n }\r\n\r\n // Get server-rendered props\r\n const serverProps = window.__FLEXI_DATA__?.props || {};\r\n const mergedProps = { ...serverProps, ...props };\r\n\r\n try {\r\n hydrateRoot(root, React.createElement(App, mergedProps));\r\n } catch (error) {\r\n console.error('Hydration failed, falling back to full render:', error);\r\n createRoot(root).render(React.createElement(App, mergedProps));\r\n }\r\n}\r\n\r\n/**\r\n * Hydrates all islands on the page\r\n */\r\nexport async function hydrateAllIslands(islandModules) {\r\n const islands = document.querySelectorAll('[data-island]');\r\n\r\n for (const element of islands) {\r\n if (element.hasAttribute('data-hydrated')) continue;\r\n\r\n const islandId = element.getAttribute('data-island');\r\n const islandName = element.getAttribute('data-island-name');\r\n const propsJson = element.getAttribute('data-island-props');\r\n\r\n try {\r\n const props = propsJson ? JSON.parse(propsJson) : {};\r\n const module = islandModules[islandName];\r\n\r\n if (module) {\r\n hydrateIsland(islandId, module.default || module, props);\r\n }\r\n } catch (error) {\r\n console.error(`Failed to hydrate island ${islandName}:`, error);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Progressive hydration based on visibility\r\n */\r\nexport function setupProgressiveHydration(islandModules) {\r\n const observer = new IntersectionObserver(\r\n (entries) => {\r\n entries.forEach(async (entry) => {\r\n if (!entry.isIntersecting) return;\r\n\r\n const element = entry.target;\r\n if (element.hasAttribute('data-hydrated')) return;\r\n\r\n const islandName = element.getAttribute('data-island-name');\r\n const islandId = element.getAttribute('data-island');\r\n const propsJson = element.getAttribute('data-island-props');\r\n\r\n try {\r\n const props = propsJson ? JSON.parse(propsJson) : {};\r\n const module = await islandModules[islandName]();\r\n\r\n hydrateIsland(islandId, module.default || module, props);\r\n observer.unobserve(element);\r\n } catch (error) {\r\n console.error(`Failed to hydrate island ${islandName}:`, error);\r\n }\r\n });\r\n },\r\n { rootMargin: '50px' }\r\n );\r\n\r\n document.querySelectorAll('[data-island]:not([data-hydrated])').forEach(el => {\r\n observer.observe(el);\r\n });\r\n\r\n return observer;\r\n}\r\n\r\nexport default {\r\n hydrateIsland,\r\n hydrateApp,\r\n hydrateAllIslands,\r\n setupProgressiveHydration\r\n};\r\n","/**\r\n * FlexiReact Client Navigation\r\n * Client-side navigation with prefetching\r\n */\r\n\r\nimport React from 'react';\r\n\r\n// Navigation state\r\nconst navigationState: {\r\n listeners: Set<(url: string) => void>;\r\n prefetched: Set<string>;\r\n} = {\r\n listeners: new Set(),\r\n prefetched: new Set()\r\n};\r\n\r\n/**\r\n * Navigates to a new URL\r\n */\r\ninterface NavigateOptions {\r\n replace?: boolean;\r\n scroll?: boolean;\r\n}\r\n\r\nexport function navigate(url: string, options: NavigateOptions = {}) {\r\n const { replace = false, scroll = true } = options;\r\n\r\n if (replace) {\r\n window.history.replaceState({}, '', url);\r\n } else {\r\n window.history.pushState({}, '', url);\r\n }\r\n\r\n // Dispatch navigation event\r\n window.dispatchEvent(new CustomEvent('flexi:navigate', {\r\n detail: { url, replace, scroll }\r\n }));\r\n\r\n // Scroll to top if needed\r\n if (scroll) {\r\n window.scrollTo(0, 0);\r\n }\r\n\r\n // Notify listeners\r\n navigationState.listeners.forEach(listener => listener(url));\r\n\r\n // Fetch and render new page\r\n return fetchAndRender(url);\r\n}\r\n\r\n/**\r\n * Prefetches a URL for faster navigation\r\n */\r\nexport function prefetch(url) {\r\n if (navigationState.prefetched.has(url)) {\r\n return Promise.resolve();\r\n }\r\n\r\n navigationState.prefetched.add(url);\r\n\r\n // Create a link element for prefetching\r\n const link = document.createElement('link');\r\n link.rel = 'prefetch';\r\n link.href = url;\r\n document.head.appendChild(link);\r\n\r\n return Promise.resolve();\r\n}\r\n\r\n/**\r\n * Fetches and renders a new page\r\n */\r\nasync function fetchAndRender(url) {\r\n try {\r\n const response = await fetch(url, {\r\n headers: {\r\n 'X-Flexi-Navigation': 'true'\r\n }\r\n });\r\n\r\n if (!response.ok) {\r\n throw new Error(`Navigation failed: ${response.status}`);\r\n }\r\n\r\n const html = await response.text();\r\n \r\n // Parse the HTML\r\n const parser = new DOMParser();\r\n const doc = parser.parseFromString(html, 'text/html');\r\n\r\n // Update the page content\r\n const newRoot = doc.getElementById('root');\r\n const currentRoot = document.getElementById('root');\r\n\r\n if (newRoot && currentRoot) {\r\n currentRoot.innerHTML = newRoot.innerHTML;\r\n }\r\n\r\n // Update the title\r\n document.title = doc.title;\r\n\r\n // Update meta tags\r\n updateMetaTags(doc);\r\n\r\n // Re-hydrate islands\r\n window.dispatchEvent(new CustomEvent('flexi:pageload'));\r\n\r\n } catch (error) {\r\n console.error('Navigation error:', error);\r\n // Fallback to full page navigation\r\n window.location.href = url;\r\n }\r\n}\r\n\r\n/**\r\n * Updates meta tags from new document\r\n */\r\nfunction updateMetaTags(doc) {\r\n // Remove old meta tags\r\n document.querySelectorAll('meta[data-flexi]').forEach(el => el.remove());\r\n\r\n // Add new meta tags\r\n doc.querySelectorAll('meta').forEach(meta => {\r\n if (meta.name || meta.property) {\r\n const newMeta = meta.cloneNode(true);\r\n newMeta.setAttribute('data-flexi', 'true');\r\n document.head.appendChild(newMeta);\r\n }\r\n });\r\n}\r\n\r\n/**\r\n * Link component for client-side navigation\r\n */\r\nexport function Link({ href, children, prefetch: shouldPrefetch = true, replace = false, className, ...props }) {\r\n const handleClick = (e) => {\r\n // Allow normal navigation for external links or modified clicks\r\n if (\r\n e.ctrlKey ||\r\n e.metaKey ||\r\n e.shiftKey ||\r\n e.button !== 0 ||\r\n href.startsWith('http') ||\r\n href.startsWith('//')\r\n ) {\r\n return;\r\n }\r\n\r\n e.preventDefault();\r\n navigate(href, { replace });\r\n };\r\n\r\n const handleMouseEnter = () => {\r\n if (shouldPrefetch) {\r\n prefetch(href);\r\n }\r\n };\r\n\r\n return React.createElement('a', {\r\n href,\r\n onClick: handleClick,\r\n onMouseEnter: handleMouseEnter,\r\n className,\r\n ...props\r\n }, children);\r\n}\r\n\r\n/**\r\n * Hook to listen for navigation events\r\n */\r\nexport function useNavigation() {\r\n const [pathname, setPathname] = React.useState(\r\n typeof window !== 'undefined' ? window.location.pathname : '/'\r\n );\r\n\r\n React.useEffect(() => {\r\n const handleNavigation = (url) => {\r\n setPathname(new URL(url, window.location.origin).pathname);\r\n };\r\n\r\n navigationState.listeners.add(handleNavigation);\r\n\r\n const handlePopState = () => {\r\n setPathname(window.location.pathname);\r\n navigationState.listeners.forEach(listener => listener(window.location.pathname));\r\n };\r\n\r\n window.addEventListener('popstate', handlePopState);\r\n\r\n return () => {\r\n navigationState.listeners.delete(handleNavigation);\r\n window.removeEventListener('popstate', handlePopState);\r\n };\r\n }, []);\r\n\r\n return { pathname, navigate, prefetch };\r\n}\r\n\r\n// Setup popstate listener\r\nif (typeof window !== 'undefined') {\r\n window.addEventListener('popstate', () => {\r\n const url = window.location.pathname + window.location.search;\r\n navigationState.listeners.forEach(listener => listener(url));\r\n });\r\n}\r\n\r\nexport default {\r\n navigate,\r\n prefetch,\r\n Link,\r\n useNavigation\r\n};\r\n","/**\r\n * FlexiReact Client Islands\r\n * Client-side island utilities\r\n */\r\n\r\nimport React from 'react';\r\n\r\n/**\r\n * Hook to check if component is hydrated\r\n */\r\nexport function useIsland() {\r\n const [isHydrated, setIsHydrated] = React.useState(false);\r\n\r\n React.useEffect(() => {\r\n setIsHydrated(true);\r\n }, []);\r\n\r\n return { isHydrated };\r\n}\r\n\r\n/**\r\n * Island boundary component\r\n * Wraps interactive components for partial hydration\r\n */\r\nexport function IslandBoundary({ children, fallback = null, name = 'island' }) {\r\n const { isHydrated } = useIsland();\r\n\r\n // On server or before hydration, render children normally\r\n // The server will wrap this in the island marker\r\n if (!isHydrated && fallback) {\r\n return fallback;\r\n }\r\n\r\n return React.createElement('div', {\r\n 'data-island-boundary': name,\r\n children\r\n });\r\n}\r\n\r\n/**\r\n * Creates a lazy-loaded island\r\n */\r\ninterface LazyIslandOptions {\r\n fallback?: React.ReactNode;\r\n name?: string;\r\n}\r\n\r\nexport function createClientIsland(loader: () => Promise<any>, options: LazyIslandOptions = {}) {\r\n const { fallback = null, name = 'lazy-island' } = options;\r\n\r\n return function LazyIsland(props) {\r\n const [Component, setComponent] = React.useState(null);\r\n const [error, setError] = React.useState(null);\r\n\r\n React.useEffect(() => {\r\n loader()\r\n .then(mod => setComponent(() => mod.default || mod))\r\n .catch(err => setError(err));\r\n }, []);\r\n\r\n if (error) {\r\n return React.createElement('div', {\r\n className: 'island-error',\r\n children: `Failed to load ${name}`\r\n });\r\n }\r\n\r\n if (!Component) {\r\n return fallback || React.createElement('div', {\r\n className: 'island-loading',\r\n children: 'Loading...'\r\n });\r\n }\r\n\r\n return React.createElement(Component, props);\r\n };\r\n}\r\n\r\n/**\r\n * Island with interaction trigger\r\n * Only hydrates when user interacts\r\n */\r\nexport function InteractiveIsland({ children, trigger = 'click', fallback }) {\r\n const [shouldHydrate, setShouldHydrate] = React.useState(false);\r\n const ref = React.useRef(null);\r\n\r\n React.useEffect(() => {\r\n const element = ref.current;\r\n if (!element) return;\r\n\r\n const handleInteraction = () => {\r\n setShouldHydrate(true);\r\n };\r\n\r\n element.addEventListener(trigger, handleInteraction, { once: true });\r\n\r\n return () => {\r\n element.removeEventListener(trigger, handleInteraction);\r\n };\r\n }, [trigger]);\r\n\r\n if (!shouldHydrate) {\r\n return React.createElement('div', {\r\n ref,\r\n 'data-interactive-island': 'true',\r\n children: fallback || children\r\n });\r\n }\r\n\r\n return children;\r\n}\r\n\r\n/**\r\n * Media query island\r\n * Only hydrates when media query matches\r\n */\r\nexport function MediaIsland({ children, query, fallback }) {\r\n const [matches, setMatches] = React.useState(false);\r\n\r\n React.useEffect(() => {\r\n const mediaQuery = window.matchMedia(query);\r\n setMatches(mediaQuery.matches);\r\n\r\n const handler = (e) => setMatches(e.matches);\r\n mediaQuery.addEventListener('change', handler);\r\n\r\n return () => mediaQuery.removeEventListener('change', handler);\r\n }, [query]);\r\n\r\n if (!matches) {\r\n return fallback || null;\r\n }\r\n\r\n return children;\r\n}\r\n\r\nexport default {\r\n useIsland,\r\n IslandBoundary,\r\n createClientIsland,\r\n InteractiveIsland,\r\n MediaIsland\r\n};\r\n","'use client';\r\n\r\n/**\r\n * FlexiReact Link Component\r\n * Enhanced link with prefetching, client-side navigation, and loading states\r\n */\r\n\r\nimport React, { useCallback, useEffect, useRef, useState } from 'react';\r\n\r\nexport interface LinkProps extends Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> {\r\n /** The URL to navigate to */\r\n href: string;\r\n /** Prefetch the page on hover/visibility */\r\n prefetch?: boolean | 'hover' | 'viewport';\r\n /** Replace the current history entry instead of pushing */\r\n replace?: boolean;\r\n /** Scroll to top after navigation */\r\n scroll?: boolean;\r\n /** Show loading indicator while navigating */\r\n showLoading?: boolean;\r\n /** Custom loading component */\r\n loadingComponent?: React.ReactNode;\r\n /** Callback when navigation starts */\r\n onNavigationStart?: () => void;\r\n /** Callback when navigation ends */\r\n onNavigationEnd?: () => void;\r\n /** \r\n * Ref to the anchor element (React 19: ref as regular prop)\r\n * No forwardRef wrapper needed in React 19\r\n */\r\n ref?: React.Ref<HTMLAnchorElement>;\r\n /** Children */\r\n children: React.ReactNode;\r\n}\r\n\r\n// Prefetch cache to avoid duplicate requests\r\nconst prefetchCache = new Set<string>();\r\n\r\n// Prefetch a URL\r\nasync function prefetchUrl(url: string): Promise<void> {\r\n if (prefetchCache.has(url)) return;\r\n\r\n try {\r\n // Mark as prefetched immediately to prevent duplicate requests\r\n prefetchCache.add(url);\r\n\r\n // Use link preload for better browser optimization\r\n const link = document.createElement('link');\r\n link.rel = 'prefetch';\r\n link.href = url;\r\n link.as = 'document';\r\n document.head.appendChild(link);\r\n\r\n // Also fetch the page to warm the cache\r\n const controller = new AbortController();\r\n const timeoutId = setTimeout(() => controller.abort(), 5000);\r\n\r\n await fetch(url, {\r\n method: 'GET',\r\n credentials: 'same-origin',\r\n signal: controller.signal,\r\n headers: {\r\n 'X-Flexi-Prefetch': '1',\r\n 'Accept': 'text/html'\r\n }\r\n });\r\n\r\n clearTimeout(timeoutId);\r\n } catch (error) {\r\n // Remove from cache on error so it can be retried\r\n prefetchCache.delete(url);\r\n }\r\n}\r\n\r\n// Check if URL is internal\r\nfunction isInternalUrl(url: string): boolean {\r\n if (url.startsWith('/')) return true;\r\n if (url.startsWith('#')) return true;\r\n\r\n try {\r\n const parsed = new URL(url, window.location.origin);\r\n return parsed.origin === window.location.origin;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n// Navigate to a URL\r\nfunction navigate(url: string, options: { replace?: boolean; scroll?: boolean } = {}): void {\r\n const { replace = false, scroll = true } = options;\r\n\r\n if (replace) {\r\n window.history.replaceState({}, '', url);\r\n } else {\r\n window.history.pushState({}, '', url);\r\n }\r\n\r\n // Dispatch popstate event to trigger any listeners\r\n window.dispatchEvent(new PopStateEvent('popstate', { state: {} }));\r\n\r\n // Scroll to top if requested\r\n if (scroll) {\r\n window.scrollTo({ top: 0, behavior: 'smooth' });\r\n }\r\n}\r\n\r\n/**\r\n * Link component with prefetching and client-side navigation\r\n * \r\n * @example\r\n * ```tsx\r\n * import { Link } from '@flexireact/core/client';\r\n * \r\n * // Basic usage\r\n * <Link href=\"/about\">About</Link>\r\n * \r\n * // With prefetch on hover\r\n * <Link href=\"/products\" prefetch=\"hover\">Products</Link>\r\n * \r\n * // With prefetch on viewport visibility\r\n * <Link href=\"/contact\" prefetch=\"viewport\">Contact</Link>\r\n * \r\n * // Replace history instead of push\r\n * <Link href=\"/login\" replace>Login</Link>\r\n * \r\n * // Disable scroll to top\r\n * <Link href=\"/section#anchor\" scroll={false}>Go to section</Link>\r\n * ```\r\n */\r\nexport function Link({\r\n href,\r\n prefetch = true,\r\n replace = false,\r\n scroll = true,\r\n showLoading = false,\r\n loadingComponent,\r\n onNavigationStart,\r\n onNavigationEnd,\r\n children,\r\n className,\r\n onClick,\r\n onMouseEnter,\r\n onFocus,\r\n ...props\r\n}: LinkProps) {\r\n const [isNavigating, setIsNavigating] = useState(false);\r\n const linkRef = useRef<HTMLAnchorElement>(null);\r\n const hasPrefetched = useRef(false);\r\n\r\n // Prefetch on viewport visibility\r\n useEffect(() => {\r\n if (prefetch !== 'viewport' && prefetch !== true) return;\r\n if (!isInternalUrl(href)) return;\r\n if (hasPrefetched.current) return;\r\n\r\n const observer = new IntersectionObserver(\r\n (entries) => {\r\n entries.forEach((entry) => {\r\n if (entry.isIntersecting) {\r\n prefetchUrl(href);\r\n hasPrefetched.current = true;\r\n observer.disconnect();\r\n }\r\n });\r\n },\r\n { rootMargin: '200px' }\r\n );\r\n\r\n if (linkRef.current) {\r\n observer.observe(linkRef.current);\r\n }\r\n\r\n return () => observer.disconnect();\r\n }, [href, prefetch]);\r\n\r\n // Handle hover prefetch\r\n const handleMouseEnter = useCallback(\r\n (e: React.MouseEvent<HTMLAnchorElement>) => {\r\n onMouseEnter?.(e);\r\n\r\n if ((prefetch === 'hover' || prefetch === true) && isInternalUrl(href)) {\r\n prefetchUrl(href);\r\n }\r\n },\r\n [href, prefetch, onMouseEnter]\r\n );\r\n\r\n // Handle focus prefetch (for keyboard navigation)\r\n const handleFocus = useCallback(\r\n (e: React.FocusEvent<HTMLAnchorElement>) => {\r\n onFocus?.(e);\r\n\r\n if ((prefetch === 'hover' || prefetch === true) && isInternalUrl(href)) {\r\n prefetchUrl(href);\r\n }\r\n },\r\n [href, prefetch, onFocus]\r\n );\r\n\r\n // Handle click for client-side navigation\r\n const handleClick = useCallback(\r\n async (e: React.MouseEvent<HTMLAnchorElement>) => {\r\n onClick?.(e);\r\n\r\n // Don't handle if default was prevented\r\n if (e.defaultPrevented) return;\r\n\r\n // Don't handle if modifier keys are pressed (open in new tab, etc.)\r\n if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;\r\n\r\n // Don't handle external URLs\r\n if (!isInternalUrl(href)) return;\r\n\r\n // Don't handle if target is set\r\n if (props.target && props.target !== '_self') return;\r\n\r\n // Prevent default navigation\r\n e.preventDefault();\r\n\r\n // Start navigation\r\n setIsNavigating(true);\r\n onNavigationStart?.();\r\n\r\n try {\r\n // Fetch the new page\r\n const response = await fetch(href, {\r\n method: 'GET',\r\n credentials: 'same-origin',\r\n headers: {\r\n 'X-Flexi-Navigation': '1',\r\n 'Accept': 'text/html'\r\n }\r\n });\r\n\r\n if (response.ok) {\r\n const html = await response.text();\r\n\r\n // Parse and update the page\r\n const parser = new DOMParser();\r\n const doc = parser.parseFromString(html, 'text/html');\r\n\r\n // Update title\r\n const newTitle = doc.querySelector('title')?.textContent;\r\n if (newTitle) {\r\n document.title = newTitle;\r\n }\r\n\r\n // Update body content (or specific container)\r\n const newContent = doc.querySelector('#root') || doc.body;\r\n const currentContent = document.querySelector('#root') || document.body;\r\n\r\n if (newContent && currentContent) {\r\n currentContent.innerHTML = newContent.innerHTML;\r\n }\r\n\r\n // Update URL\r\n navigate(href, { replace, scroll });\r\n } else {\r\n // Fallback to regular navigation on error\r\n window.location.href = href;\r\n }\r\n } catch (error) {\r\n // Fallback to regular navigation on error\r\n window.location.href = href;\r\n } finally {\r\n setIsNavigating(false);\r\n onNavigationEnd?.();\r\n }\r\n },\r\n [href, replace, scroll, onClick, onNavigationStart, onNavigationEnd, props.target]\r\n );\r\n\r\n return (\r\n <a\r\n ref={linkRef}\r\n href={href}\r\n className={className}\r\n onClick={handleClick}\r\n onMouseEnter={handleMouseEnter}\r\n onFocus={handleFocus}\r\n data-prefetch={prefetch}\r\n data-navigating={isNavigating || undefined}\r\n {...props}\r\n >\r\n {showLoading && isNavigating ? (\r\n loadingComponent || (\r\n <span className=\"flexi-link-loading\">\r\n <span className=\"flexi-link-spinner\" />\r\n {children}\r\n </span>\r\n )\r\n ) : (\r\n children\r\n )}\r\n </a>\r\n );\r\n}\r\n\r\n/**\r\n * Programmatic navigation function\r\n * \r\n * @example\r\n * ```tsx\r\n * import { useRouter } from '@flexireact/core/client';\r\n * \r\n * function MyComponent() {\r\n * const router = useRouter();\r\n * \r\n * const handleClick = () => {\r\n * router.push('/dashboard');\r\n * };\r\n * \r\n * return <button onClick={handleClick}>Go to Dashboard</button>;\r\n * }\r\n * ```\r\n */\r\nexport function useRouter() {\r\n return {\r\n push(url: string, options?: { scroll?: boolean }) {\r\n navigate(url, { replace: false, scroll: options?.scroll ?? true });\r\n // Trigger page reload for now (full SPA navigation requires more work)\r\n window.location.href = url;\r\n },\r\n\r\n replace(url: string, options?: { scroll?: boolean }) {\r\n navigate(url, { replace: true, scroll: options?.scroll ?? true });\r\n window.location.href = url;\r\n },\r\n\r\n back() {\r\n window.history.back();\r\n },\r\n\r\n forward() {\r\n window.history.forward();\r\n },\r\n\r\n prefetch(url: string) {\r\n if (isInternalUrl(url)) {\r\n prefetchUrl(url);\r\n }\r\n },\r\n\r\n refresh() {\r\n window.location.reload();\r\n }\r\n };\r\n}\r\n\r\nexport default Link;\r\n"],"mappings":"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","names":["React","React","navigate","Link","prefetch"]}
@@ -2911,6 +2911,9 @@ function isMethod(request, method) {
2911
2911
  }
2912
2912
 
2913
2913
  // core/actions/index.ts
2914
+ import { useActionState, useOptimistic } from "react";
2915
+ import { useFormStatus } from "react-dom";
2916
+ import { useActionState as useActionStateReact } from "react";
2914
2917
  globalThis.__FLEXI_ACTIONS__ = globalThis.__FLEXI_ACTIONS__ || {};
2915
2918
  globalThis.__FLEXI_ACTION_CONTEXT__ = null;
2916
2919
  function serverAction(fn, actionId) {
@@ -3075,11 +3078,18 @@ function formAction(action) {
3075
3078
  }
3076
3079
  };
3077
3080
  }
3081
+ function useFlexiAction(action, initialState, permalink) {
3082
+ return useActionStateReact(action, initialState, permalink);
3083
+ }
3078
3084
  function createFormState(action, initialState = null) {
3085
+ if (typeof window !== "undefined" && process.env.NODE_ENV === "development") {
3086
+ console.warn(
3087
+ "[FlexiReact] createFormState is deprecated. Use useActionState from React 19 instead.\nSee migration guide: https://flexireact.dev/docs/react-19-migration"
3088
+ );
3089
+ }
3079
3090
  return {
3080
3091
  action,
3081
3092
  initialState,
3082
- // This will be enhanced on the client
3083
3093
  pending: false,
3084
3094
  error: null,
3085
3095
  data: initialState
@@ -5749,8 +5759,34 @@ var jsonLd = {
5749
5759
  })
5750
5760
  };
5751
5761
 
5762
+ // core/index.ts
5763
+ import { useActionState as useActionState3, useOptimistic as useOptimistic3 } from "react";
5764
+ import { useFormStatus as useFormStatus3 } from "react-dom";
5765
+
5766
+ // core/hooks/index.ts
5767
+ import { useActionState as useActionState2, useOptimistic as useOptimistic2 } from "react";
5768
+ import { useFormStatus as useFormStatus2 } from "react-dom";
5769
+ import { use } from "react";
5770
+
5771
+ // core/client/Link.tsx
5772
+ import { useCallback, useEffect, useRef, useState } from "react";
5773
+ import { jsx, jsxs } from "react/jsx-runtime";
5774
+
5775
+ // core/hooks/index.ts
5776
+ import { use as use2, useOptimistic as useOptimisticReact } from "react";
5777
+ function useAsyncData(promise) {
5778
+ return use2(promise);
5779
+ }
5780
+ function useOptimisticMutation(currentState, updateFn) {
5781
+ return useOptimisticReact(currentState, updateFn);
5782
+ }
5783
+ function preloadResource(fetcher) {
5784
+ const promise = fetcher();
5785
+ return promise;
5786
+ }
5787
+
5752
5788
  // core/devtools/index.ts
5753
- import React7 from "react";
5789
+ import React8 from "react";
5754
5790
  var devToolsState = {
5755
5791
  routes: [],
5756
5792
  components: /* @__PURE__ */ new Map(),
@@ -5928,24 +5964,24 @@ function initNetworkInterceptor() {
5928
5964
  };
5929
5965
  }
5930
5966
  function DevToolsOverlay() {
5931
- const [state, setState] = React7.useState({
5967
+ const [state, setState] = React8.useState({
5932
5968
  enabled: true,
5933
5969
  position: "bottom-right",
5934
5970
  expanded: false,
5935
5971
  activeTab: "routes",
5936
5972
  theme: "dark"
5937
5973
  });
5938
- const [data, setData] = React7.useState(devtools.getState());
5939
- React7.useEffect(() => {
5974
+ const [data, setData] = React8.useState(devtools.getState());
5975
+ React8.useEffect(() => {
5940
5976
  return devtools.subscribe(() => {
5941
5977
  setData(devtools.getState());
5942
5978
  });
5943
5979
  }, []);
5944
- React7.useEffect(() => {
5980
+ React8.useEffect(() => {
5945
5981
  initPerformanceMonitoring();
5946
5982
  initNetworkInterceptor();
5947
5983
  }, []);
5948
- React7.useEffect(() => {
5984
+ React8.useEffect(() => {
5949
5985
  const handler = (e) => {
5950
5986
  if (e.ctrlKey && e.shiftKey && e.key === "D") {
5951
5987
  setState((s) => ({ ...s, expanded: !s.expanded }));
@@ -5962,7 +5998,7 @@ function DevToolsOverlay() {
5962
5998
  "top-left": { top: 16, left: 16 }
5963
5999
  };
5964
6000
  if (!state.expanded) {
5965
- return React7.createElement("button", {
6001
+ return React8.createElement("button", {
5966
6002
  onClick: () => setState((s) => ({ ...s, expanded: true })),
5967
6003
  style: {
5968
6004
  position: "fixed",
@@ -5983,7 +6019,7 @@ function DevToolsOverlay() {
5983
6019
  onMouseEnter: (e) => e.target.style.transform = "scale(1.1)",
5984
6020
  onMouseLeave: (e) => e.target.style.transform = "scale(1)",
5985
6021
  title: "FlexiReact DevTools (Ctrl+Shift+D)"
5986
- }, React7.createElement("span", { style: { fontSize: 24 } }, "\u26A1"));
6022
+ }, React8.createElement("span", { style: { fontSize: 24 } }, "\u26A1"));
5987
6023
  }
5988
6024
  const tabs = [
5989
6025
  { id: "routes", label: "\u{1F5FA}\uFE0F Routes", count: data.routes.length },
@@ -5992,7 +6028,7 @@ function DevToolsOverlay() {
5992
6028
  { id: "performance", label: "\u{1F4CA} Performance", count: data.performance.length },
5993
6029
  { id: "console", label: "\u{1F4DD} Console", count: data.logs.length }
5994
6030
  ];
5995
- return React7.createElement("div", {
6031
+ return React8.createElement("div", {
5996
6032
  style: {
5997
6033
  position: "fixed",
5998
6034
  ...positionStyles[state.position],
@@ -6010,7 +6046,7 @@ function DevToolsOverlay() {
6010
6046
  }
6011
6047
  }, [
6012
6048
  // Header
6013
- React7.createElement("div", {
6049
+ React8.createElement("div", {
6014
6050
  key: "header",
6015
6051
  style: {
6016
6052
  display: "flex",
@@ -6021,14 +6057,14 @@ function DevToolsOverlay() {
6021
6057
  background: "#111"
6022
6058
  }
6023
6059
  }, [
6024
- React7.createElement("div", {
6060
+ React8.createElement("div", {
6025
6061
  key: "title",
6026
6062
  style: { display: "flex", alignItems: "center", gap: 8 }
6027
6063
  }, [
6028
- React7.createElement("span", { key: "icon" }, "\u26A1"),
6029
- React7.createElement("span", { key: "text", style: { fontWeight: 600 } }, "FlexiReact DevTools")
6064
+ React8.createElement("span", { key: "icon" }, "\u26A1"),
6065
+ React8.createElement("span", { key: "text", style: { fontWeight: 600 } }, "FlexiReact DevTools")
6030
6066
  ]),
6031
- React7.createElement("button", {
6067
+ React8.createElement("button", {
6032
6068
  key: "close",
6033
6069
  onClick: () => setState((s) => ({ ...s, expanded: false })),
6034
6070
  style: {
@@ -6041,7 +6077,7 @@ function DevToolsOverlay() {
6041
6077
  }, "\xD7")
6042
6078
  ]),
6043
6079
  // Tabs
6044
- React7.createElement("div", {
6080
+ React8.createElement("div", {
6045
6081
  key: "tabs",
6046
6082
  style: {
6047
6083
  display: "flex",
@@ -6050,7 +6086,7 @@ function DevToolsOverlay() {
6050
6086
  overflowX: "auto"
6051
6087
  }
6052
6088
  }, tabs.map(
6053
- (tab) => React7.createElement("button", {
6089
+ (tab) => React8.createElement("button", {
6054
6090
  key: tab.id,
6055
6091
  onClick: () => setState((s) => ({ ...s, activeTab: tab.id })),
6056
6092
  style: {
@@ -6066,7 +6102,7 @@ function DevToolsOverlay() {
6066
6102
  }, `${tab.label} (${tab.count})`)
6067
6103
  )),
6068
6104
  // Content
6069
- React7.createElement("div", {
6105
+ React8.createElement("div", {
6070
6106
  key: "content",
6071
6107
  style: {
6072
6108
  padding: 16,
@@ -6079,11 +6115,11 @@ function DevToolsOverlay() {
6079
6115
  function renderTabContent(tab, data) {
6080
6116
  switch (tab) {
6081
6117
  case "routes":
6082
- return React7.createElement(
6118
+ return React8.createElement(
6083
6119
  "div",
6084
6120
  { style: { display: "flex", flexDirection: "column", gap: 8 } },
6085
- data.routes.length === 0 ? React7.createElement("div", { style: { color: "#666", textAlign: "center", padding: 20 } }, "No routes tracked yet") : data.routes.map(
6086
- (route, i) => React7.createElement("div", {
6121
+ data.routes.length === 0 ? React8.createElement("div", { style: { color: "#666", textAlign: "center", padding: 20 } }, "No routes tracked yet") : data.routes.map(
6122
+ (route, i) => React8.createElement("div", {
6087
6123
  key: i,
6088
6124
  style: {
6089
6125
  padding: 12,
@@ -6092,8 +6128,8 @@ function renderTabContent(tab, data) {
6092
6128
  border: "1px solid #222"
6093
6129
  }
6094
6130
  }, [
6095
- React7.createElement("div", { key: "path", style: { fontWeight: 600, color: "#00FF9C" } }, route.path),
6096
- React7.createElement(
6131
+ React8.createElement("div", { key: "path", style: { fontWeight: 600, color: "#00FF9C" } }, route.path),
6132
+ React8.createElement(
6097
6133
  "div",
6098
6134
  { key: "component", style: { fontSize: 11, color: "#888", marginTop: 4 } },
6099
6135
  `Component: ${route.component} \u2022 ${route.loadTime}ms`
@@ -6102,11 +6138,11 @@ function renderTabContent(tab, data) {
6102
6138
  )
6103
6139
  );
6104
6140
  case "components":
6105
- return React7.createElement(
6141
+ return React8.createElement(
6106
6142
  "div",
6107
6143
  { style: { display: "flex", flexDirection: "column", gap: 8 } },
6108
- data.components.length === 0 ? React7.createElement("div", { style: { color: "#666", textAlign: "center", padding: 20 } }, "No components tracked") : data.components.map(
6109
- (comp, i) => React7.createElement("div", {
6144
+ data.components.length === 0 ? React8.createElement("div", { style: { color: "#666", textAlign: "center", padding: 20 } }, "No components tracked") : data.components.map(
6145
+ (comp, i) => React8.createElement("div", {
6110
6146
  key: i,
6111
6147
  style: {
6112
6148
  padding: 12,
@@ -6115,12 +6151,12 @@ function renderTabContent(tab, data) {
6115
6151
  border: "1px solid #222"
6116
6152
  }
6117
6153
  }, [
6118
- React7.createElement("div", {
6154
+ React8.createElement("div", {
6119
6155
  key: "name",
6120
6156
  style: { display: "flex", alignItems: "center", gap: 8 }
6121
6157
  }, [
6122
- React7.createElement("span", { key: "text", style: { fontWeight: 600 } }, comp.name),
6123
- comp.isIsland && React7.createElement("span", {
6158
+ React8.createElement("span", { key: "text", style: { fontWeight: 600 } }, comp.name),
6159
+ comp.isIsland && React8.createElement("span", {
6124
6160
  key: "island",
6125
6161
  style: {
6126
6162
  fontSize: 10,
@@ -6131,7 +6167,7 @@ function renderTabContent(tab, data) {
6131
6167
  }
6132
6168
  }, "Island")
6133
6169
  ]),
6134
- React7.createElement(
6170
+ React8.createElement(
6135
6171
  "div",
6136
6172
  { key: "info", style: { fontSize: 11, color: "#888", marginTop: 4 } },
6137
6173
  `Renders: ${comp.renderCount} \u2022 Last: ${new Date(comp.lastRenderTime).toLocaleTimeString()}`
@@ -6140,11 +6176,11 @@ function renderTabContent(tab, data) {
6140
6176
  )
6141
6177
  );
6142
6178
  case "network":
6143
- return React7.createElement(
6179
+ return React8.createElement(
6144
6180
  "div",
6145
6181
  { style: { display: "flex", flexDirection: "column", gap: 8 } },
6146
- data.network.length === 0 ? React7.createElement("div", { style: { color: "#666", textAlign: "center", padding: 20 } }, "No requests yet") : data.network.map(
6147
- (req, i) => React7.createElement("div", {
6182
+ data.network.length === 0 ? React8.createElement("div", { style: { color: "#666", textAlign: "center", padding: 20 } }, "No requests yet") : data.network.map(
6183
+ (req, i) => React8.createElement("div", {
6148
6184
  key: i,
6149
6185
  style: {
6150
6186
  padding: 12,
@@ -6153,11 +6189,11 @@ function renderTabContent(tab, data) {
6153
6189
  border: "1px solid #222"
6154
6190
  }
6155
6191
  }, [
6156
- React7.createElement("div", {
6192
+ React8.createElement("div", {
6157
6193
  key: "url",
6158
6194
  style: { display: "flex", alignItems: "center", gap: 8 }
6159
6195
  }, [
6160
- React7.createElement("span", {
6196
+ React8.createElement("span", {
6161
6197
  key: "method",
6162
6198
  style: {
6163
6199
  fontSize: 10,
@@ -6168,7 +6204,7 @@ function renderTabContent(tab, data) {
6168
6204
  fontWeight: 600
6169
6205
  }
6170
6206
  }, req.method),
6171
- React7.createElement("span", {
6207
+ React8.createElement("span", {
6172
6208
  key: "status",
6173
6209
  style: {
6174
6210
  fontSize: 10,
@@ -6178,12 +6214,12 @@ function renderTabContent(tab, data) {
6178
6214
  borderRadius: 4
6179
6215
  }
6180
6216
  }, req.status || "ERR"),
6181
- React7.createElement("span", {
6217
+ React8.createElement("span", {
6182
6218
  key: "path",
6183
6219
  style: { fontSize: 12, color: "#fff", overflow: "hidden", textOverflow: "ellipsis" }
6184
6220
  }, new URL(req.url, "http://localhost").pathname)
6185
6221
  ]),
6186
- React7.createElement(
6222
+ React8.createElement(
6187
6223
  "div",
6188
6224
  { key: "info", style: { fontSize: 11, color: "#888", marginTop: 4 } },
6189
6225
  `${req.duration}ms \u2022 ${formatBytes2(req.size)}`
@@ -6192,11 +6228,11 @@ function renderTabContent(tab, data) {
6192
6228
  )
6193
6229
  );
6194
6230
  case "performance":
6195
- return React7.createElement(
6231
+ return React8.createElement(
6196
6232
  "div",
6197
6233
  { style: { display: "flex", flexDirection: "column", gap: 8 } },
6198
- data.performance.length === 0 ? React7.createElement("div", { style: { color: "#666", textAlign: "center", padding: 20 } }, "Collecting metrics...") : data.performance.map(
6199
- (metric, i) => React7.createElement("div", {
6234
+ data.performance.length === 0 ? React8.createElement("div", { style: { color: "#666", textAlign: "center", padding: 20 } }, "Collecting metrics...") : data.performance.map(
6235
+ (metric, i) => React8.createElement("div", {
6200
6236
  key: i,
6201
6237
  style: {
6202
6238
  padding: 12,
@@ -6208,14 +6244,14 @@ function renderTabContent(tab, data) {
6208
6244
  alignItems: "center"
6209
6245
  }
6210
6246
  }, [
6211
- React7.createElement("span", { key: "name", style: { fontWeight: 600 } }, metric.name),
6212
- React7.createElement("div", { key: "value", style: { display: "flex", alignItems: "center", gap: 8 } }, [
6213
- React7.createElement(
6247
+ React8.createElement("span", { key: "name", style: { fontWeight: 600 } }, metric.name),
6248
+ React8.createElement("div", { key: "value", style: { display: "flex", alignItems: "center", gap: 8 } }, [
6249
+ React8.createElement(
6214
6250
  "span",
6215
6251
  { key: "num" },
6216
6252
  metric.name === "CLS" ? metric.value.toFixed(3) : `${Math.round(metric.value)}ms`
6217
6253
  ),
6218
- React7.createElement("span", {
6254
+ React8.createElement("span", {
6219
6255
  key: "rating",
6220
6256
  style: {
6221
6257
  width: 8,
@@ -6229,11 +6265,11 @@ function renderTabContent(tab, data) {
6229
6265
  )
6230
6266
  );
6231
6267
  case "console":
6232
- return React7.createElement(
6268
+ return React8.createElement(
6233
6269
  "div",
6234
6270
  { style: { display: "flex", flexDirection: "column", gap: 4 } },
6235
- data.logs.length === 0 ? React7.createElement("div", { style: { color: "#666", textAlign: "center", padding: 20 } }, "No logs yet") : data.logs.map(
6236
- (log, i) => React7.createElement("div", {
6271
+ data.logs.length === 0 ? React8.createElement("div", { style: { color: "#666", textAlign: "center", padding: 20 } }, "No logs yet") : data.logs.map(
6272
+ (log, i) => React8.createElement("div", {
6237
6273
  key: i,
6238
6274
  style: {
6239
6275
  padding: "8px 12px",
@@ -6244,7 +6280,7 @@ function renderTabContent(tab, data) {
6244
6280
  color: log.level === "error" ? "#EF4444" : log.level === "warn" ? "#F59E0B" : "#888"
6245
6281
  }
6246
6282
  }, [
6247
- React7.createElement(
6283
+ React8.createElement(
6248
6284
  "span",
6249
6285
  { key: "time", style: { color: "#444", marginRight: 8 } },
6250
6286
  new Date(log.timestamp).toLocaleTimeString()
@@ -6254,7 +6290,7 @@ function renderTabContent(tab, data) {
6254
6290
  )
6255
6291
  );
6256
6292
  default:
6257
- return React7.createElement("div", {}, "Unknown tab");
6293
+ return React8.createElement("div", {}, "Unknown tab");
6258
6294
  }
6259
6295
  }
6260
6296
  function formatBytes2(bytes) {
@@ -6266,7 +6302,7 @@ function formatBytes2(bytes) {
6266
6302
  }
6267
6303
 
6268
6304
  // core/index.ts
6269
- var VERSION = "3.0.0";
6305
+ var VERSION = "3.1.0";
6270
6306
  var core_default = {
6271
6307
  VERSION,
6272
6308
  createServer
@@ -6384,6 +6420,7 @@ export {
6384
6420
  parseSearchParams,
6385
6421
  pluginManager,
6386
6422
  pprFetch,
6423
+ preloadResource,
6387
6424
  prerenderWithPPR,
6388
6425
  processServerComponent,
6389
6426
  reactCache,
@@ -6407,6 +6444,12 @@ export {
6407
6444
  text,
6408
6445
  unstable_cache,
6409
6446
  useActionContext,
6447
+ useActionState3 as useActionState,
6448
+ useAsyncData,
6449
+ useFlexiAction,
6450
+ useFormStatus3 as useFormStatus,
6451
+ useOptimistic3 as useOptimistic,
6452
+ useOptimisticMutation,
6410
6453
  useParams,
6411
6454
  usePathname,
6412
6455
  useQuery,