@flexireact/core 3.0.1 → 3.0.3

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.
Files changed (55) hide show
  1. package/dist/cli/index.js +1514 -0
  2. package/dist/cli/index.js.map +1 -0
  3. package/dist/core/client/index.js +373 -0
  4. package/dist/core/client/index.js.map +1 -0
  5. package/dist/core/index.js +6415 -0
  6. package/dist/core/index.js.map +1 -0
  7. package/dist/core/server/index.js +3094 -0
  8. package/dist/core/server/index.js.map +1 -0
  9. package/package.json +80 -80
  10. package/bin/flexireact.js +0 -23
  11. package/cli/generators.ts +0 -616
  12. package/cli/index.ts +0 -1182
  13. package/core/actions/index.ts +0 -364
  14. package/core/api.ts +0 -143
  15. package/core/build/index.ts +0 -425
  16. package/core/cli/logger.ts +0 -353
  17. package/core/client/Link.tsx +0 -345
  18. package/core/client/hydration.ts +0 -147
  19. package/core/client/index.ts +0 -12
  20. package/core/client/islands.ts +0 -143
  21. package/core/client/navigation.ts +0 -212
  22. package/core/client/runtime.ts +0 -52
  23. package/core/config.ts +0 -116
  24. package/core/context.ts +0 -83
  25. package/core/dev.ts +0 -47
  26. package/core/devtools/index.ts +0 -644
  27. package/core/edge/cache.ts +0 -344
  28. package/core/edge/fetch-polyfill.ts +0 -247
  29. package/core/edge/handler.ts +0 -248
  30. package/core/edge/index.ts +0 -81
  31. package/core/edge/ppr.ts +0 -264
  32. package/core/edge/runtime.ts +0 -161
  33. package/core/font/index.ts +0 -306
  34. package/core/helpers.ts +0 -494
  35. package/core/image/index.ts +0 -413
  36. package/core/index.ts +0 -218
  37. package/core/islands/index.ts +0 -293
  38. package/core/loader.ts +0 -111
  39. package/core/logger.ts +0 -242
  40. package/core/metadata/index.ts +0 -622
  41. package/core/middleware/index.ts +0 -416
  42. package/core/plugins/index.ts +0 -373
  43. package/core/render/index.ts +0 -1243
  44. package/core/render.ts +0 -136
  45. package/core/router/index.ts +0 -551
  46. package/core/router.ts +0 -141
  47. package/core/rsc/index.ts +0 -199
  48. package/core/server/index.ts +0 -779
  49. package/core/server.ts +0 -203
  50. package/core/ssg/index.ts +0 -346
  51. package/core/start-dev.ts +0 -6
  52. package/core/start-prod.ts +0 -6
  53. package/core/tsconfig.json +0 -30
  54. package/core/types.ts +0 -239
  55. package/core/utils.ts +0 -176
@@ -0,0 +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":";AAKA,OAAO,WAAW;AAClB,SAAS,aAAa,kBAAkB;AAejC,SAAS,cAAc,UAAU,WAAW,OAAO;AACxD,QAAM,UAAU,SAAS,cAAc,iBAAiB,QAAQ,IAAI;AAEpE,MAAI,CAAC,SAAS;AACZ,YAAQ,KAAK,6BAA6B,QAAQ,EAAE;AACpD;AAAA,EACF;AAEA,MAAI,QAAQ,aAAa,eAAe,GAAG;AACzC;AAAA,EACF;AAEA,MAAI;AACF,gBAAY,SAAS,MAAM,cAAc,WAAW,KAAK,CAAC;AAC1D,YAAQ,aAAa,iBAAiB,MAAM;AAG5C,YAAQ,cAAc,IAAI,YAAY,kBAAkB;AAAA,MACtD,SAAS;AAAA,MACT,QAAQ,EAAE,UAAU,MAAM;AAAA,IAC5B,CAAC,CAAC;AAAA,EACJ,SAAS,OAAO;AACd,YAAQ,MAAM,4BAA4B,QAAQ,KAAK,KAAK;AAG5D,QAAI;AACF,iBAAW,OAAO,EAAE,OAAO,MAAM,cAAc,WAAW,KAAK,CAAC;AAChE,cAAQ,aAAa,iBAAiB,MAAM;AAAA,IAC9C,SAAS,eAAe;AACtB,cAAQ,MAAM,mCAAmC,QAAQ,KAAK,aAAa;AAAA,IAC7E;AAAA,EACF;AACF;AAKO,SAAS,WAAW,KAAK,QAAQ,CAAC,GAAG;AAC1C,QAAM,OAAO,SAAS,eAAe,MAAM;AAE3C,MAAI,CAAC,MAAM;AACT,YAAQ,MAAM,wBAAwB;AACtC;AAAA,EACF;AAGA,QAAM,cAAc,OAAO,gBAAgB,SAAS,CAAC;AACrD,QAAM,cAAc,EAAE,GAAG,aAAa,GAAG,MAAM;AAE/C,MAAI;AACF,gBAAY,MAAM,MAAM,cAAc,KAAK,WAAW,CAAC;AAAA,EACzD,SAAS,OAAO;AACd,YAAQ,MAAM,kDAAkD,KAAK;AACrE,eAAW,IAAI,EAAE,OAAO,MAAM,cAAc,KAAK,WAAW,CAAC;AAAA,EAC/D;AACF;;;ACvEA,OAAOA,YAAW;AAGlB,IAAM,kBAGF;AAAA,EACF,WAAW,oBAAI,IAAI;AAAA,EACnB,YAAY,oBAAI,IAAI;AACtB;AAUO,SAAS,SAAS,KAAa,UAA2B,CAAC,GAAG;AACnE,QAAM,EAAE,UAAU,OAAO,SAAS,KAAK,IAAI;AAE3C,MAAI,SAAS;AACX,WAAO,QAAQ,aAAa,CAAC,GAAG,IAAI,GAAG;AAAA,EACzC,OAAO;AACL,WAAO,QAAQ,UAAU,CAAC,GAAG,IAAI,GAAG;AAAA,EACtC;AAGA,SAAO,cAAc,IAAI,YAAY,kBAAkB;AAAA,IACrD,QAAQ,EAAE,KAAK,SAAS,OAAO;AAAA,EACjC,CAAC,CAAC;AAGF,MAAI,QAAQ;AACV,WAAO,SAAS,GAAG,CAAC;AAAA,EACtB;AAGA,kBAAgB,UAAU,QAAQ,cAAY,SAAS,GAAG,CAAC;AAG3D,SAAO,eAAe,GAAG;AAC3B;AAKO,SAAS,SAAS,KAAK;AAC5B,MAAI,gBAAgB,WAAW,IAAI,GAAG,GAAG;AACvC,WAAO,QAAQ,QAAQ;AAAA,EACzB;AAEA,kBAAgB,WAAW,IAAI,GAAG;AAGlC,QAAM,OAAO,SAAS,cAAc,MAAM;AAC1C,OAAK,MAAM;AACX,OAAK,OAAO;AACZ,WAAS,KAAK,YAAY,IAAI;AAE9B,SAAO,QAAQ,QAAQ;AACzB;AAKA,eAAe,eAAe,KAAK;AACjC,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,KAAK;AAAA,MAChC,SAAS;AAAA,QACP,sBAAsB;AAAA,MACxB;AAAA,IACF,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI,MAAM,sBAAsB,SAAS,MAAM,EAAE;AAAA,IACzD;AAEA,UAAM,OAAO,MAAM,SAAS,KAAK;AAGjC,UAAM,SAAS,IAAI,UAAU;AAC7B,UAAM,MAAM,OAAO,gBAAgB,MAAM,WAAW;AAGpD,UAAM,UAAU,IAAI,eAAe,MAAM;AACzC,UAAM,cAAc,SAAS,eAAe,MAAM;AAElD,QAAI,WAAW,aAAa;AAC1B,kBAAY,YAAY,QAAQ;AAAA,IAClC;AAGA,aAAS,QAAQ,IAAI;AAGrB,mBAAe,GAAG;AAGlB,WAAO,cAAc,IAAI,YAAY,gBAAgB,CAAC;AAAA,EAExD,SAAS,OAAO;AACd,YAAQ,MAAM,qBAAqB,KAAK;AAExC,WAAO,SAAS,OAAO;AAAA,EACzB;AACF;AAKA,SAAS,eAAe,KAAK;AAE3B,WAAS,iBAAiB,kBAAkB,EAAE,QAAQ,QAAM,GAAG,OAAO,CAAC;AAGvE,MAAI,iBAAiB,MAAM,EAAE,QAAQ,UAAQ;AAC3C,QAAI,KAAK,QAAQ,KAAK,UAAU;AAC9B,YAAM,UAAU,KAAK,UAAU,IAAI;AACnC,cAAQ,aAAa,cAAc,MAAM;AACzC,eAAS,KAAK,YAAY,OAAO;AAAA,IACnC;AAAA,EACF,CAAC;AACH;AAKO,SAAS,KAAK,EAAE,MAAM,UAAU,UAAU,iBAAiB,MAAM,UAAU,OAAO,WAAW,GAAG,MAAM,GAAG;AAC9G,QAAM,cAAc,CAAC,MAAM;AAEzB,QACE,EAAE,WACF,EAAE,WACF,EAAE,YACF,EAAE,WAAW,KACb,KAAK,WAAW,MAAM,KACtB,KAAK,WAAW,IAAI,GACpB;AACA;AAAA,IACF;AAEA,MAAE,eAAe;AACjB,aAAS,MAAM,EAAE,QAAQ,CAAC;AAAA,EAC5B;AAEA,QAAM,mBAAmB,MAAM;AAC7B,QAAI,gBAAgB;AAClB,eAAS,IAAI;AAAA,IACf;AAAA,EACF;AAEA,SAAOA,OAAM,cAAc,KAAK;AAAA,IAC9B;AAAA,IACA,SAAS;AAAA,IACT,cAAc;AAAA,IACd;AAAA,IACA,GAAG;AAAA,EACL,GAAG,QAAQ;AACb;AAkCA,IAAI,OAAO,WAAW,aAAa;AACjC,SAAO,iBAAiB,YAAY,MAAM;AACxC,UAAM,MAAM,OAAO,SAAS,WAAW,OAAO,SAAS;AACvD,oBAAgB,UAAU,QAAQ,cAAY,SAAS,GAAG,CAAC;AAAA,EAC7D,CAAC;AACH;;;ACvMA,OAAOC,YAAW;AAKX,SAAS,YAAY;AAC1B,QAAM,CAAC,YAAY,aAAa,IAAIA,OAAM,SAAS,KAAK;AAExD,EAAAA,OAAM,UAAU,MAAM;AACpB,kBAAc,IAAI;AAAA,EACpB,GAAG,CAAC,CAAC;AAEL,SAAO,EAAE,WAAW;AACtB;AAMO,SAAS,eAAe,EAAE,UAAU,WAAW,MAAM,OAAO,SAAS,GAAG;AAC7E,QAAM,EAAE,WAAW,IAAI,UAAU;AAIjC,MAAI,CAAC,cAAc,UAAU;AAC3B,WAAO;AAAA,EACT;AAEA,SAAOA,OAAM,cAAc,OAAO;AAAA,IAChC,wBAAwB;AAAA,IACxB;AAAA,EACF,CAAC;AACH;;;AC9BA,SAAgB,aAAa,WAAW,QAAQ,gBAAgB;AAkRtD,SACE,KADF;AA1PV,IAAM,gBAAgB,oBAAI,IAAY;AAGtC,eAAe,YAAY,KAA4B;AACrD,MAAI,cAAc,IAAI,GAAG,EAAG;AAE5B,MAAI;AAEF,kBAAc,IAAI,GAAG;AAGrB,UAAM,OAAO,SAAS,cAAc,MAAM;AAC1C,SAAK,MAAM;AACX,SAAK,OAAO;AACZ,SAAK,KAAK;AACV,aAAS,KAAK,YAAY,IAAI;AAG9B,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,YAAY,WAAW,MAAM,WAAW,MAAM,GAAG,GAAI;AAE3D,UAAM,MAAM,KAAK;AAAA,MACf,QAAQ;AAAA,MACR,aAAa;AAAA,MACb,QAAQ,WAAW;AAAA,MACnB,SAAS;AAAA,QACP,oBAAoB;AAAA,QACpB,UAAU;AAAA,MACZ;AAAA,IACF,CAAC;AAED,iBAAa,SAAS;AAAA,EACxB,SAAS,OAAO;AAEd,kBAAc,OAAO,GAAG;AAAA,EAC1B;AACF;AAGA,SAAS,cAAc,KAAsB;AAC3C,MAAI,IAAI,WAAW,GAAG,EAAG,QAAO;AAChC,MAAI,IAAI,WAAW,GAAG,EAAG,QAAO;AAEhC,MAAI;AACF,UAAM,SAAS,IAAI,IAAI,KAAK,OAAO,SAAS,MAAM;AAClD,WAAO,OAAO,WAAW,OAAO,SAAS;AAAA,EAC3C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGA,SAASC,UAAS,KAAa,UAAmD,CAAC,GAAS;AAC1F,QAAM,EAAE,UAAU,OAAO,SAAS,KAAK,IAAI;AAE3C,MAAI,SAAS;AACX,WAAO,QAAQ,aAAa,CAAC,GAAG,IAAI,GAAG;AAAA,EACzC,OAAO;AACL,WAAO,QAAQ,UAAU,CAAC,GAAG,IAAI,GAAG;AAAA,EACtC;AAGA,SAAO,cAAc,IAAI,cAAc,YAAY,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;AAGjE,MAAI,QAAQ;AACV,WAAO,SAAS,EAAE,KAAK,GAAG,UAAU,SAAS,CAAC;AAAA,EAChD;AACF;AAyBO,SAASC,MAAK;AAAA,EACnB;AAAA,EACA,UAAAC,YAAW;AAAA,EACX,UAAU;AAAA,EACV,SAAS;AAAA,EACT,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAc;AACZ,QAAM,CAAC,cAAc,eAAe,IAAI,SAAS,KAAK;AACtD,QAAM,UAAU,OAA0B,IAAI;AAC9C,QAAM,gBAAgB,OAAO,KAAK;AAGlC,YAAU,MAAM;AACd,QAAIA,cAAa,cAAcA,cAAa,KAAM;AAClD,QAAI,CAAC,cAAc,IAAI,EAAG;AAC1B,QAAI,cAAc,QAAS;AAE3B,UAAM,WAAW,IAAI;AAAA,MACnB,CAAC,YAAY;AACX,gBAAQ,QAAQ,CAAC,UAAU;AACzB,cAAI,MAAM,gBAAgB;AACxB,wBAAY,IAAI;AAChB,0BAAc,UAAU;AACxB,qBAAS,WAAW;AAAA,UACtB;AAAA,QACF,CAAC;AAAA,MACH;AAAA,MACA,EAAE,YAAY,QAAQ;AAAA,IACxB;AAEA,QAAI,QAAQ,SAAS;AACnB,eAAS,QAAQ,QAAQ,OAAO;AAAA,IAClC;AAEA,WAAO,MAAM,SAAS,WAAW;AAAA,EACnC,GAAG,CAAC,MAAMA,SAAQ,CAAC;AAGnB,QAAM,mBAAmB;AAAA,IACvB,CAAC,MAA2C;AAC1C,qBAAe,CAAC;AAEhB,WAAKA,cAAa,WAAWA,cAAa,SAAS,cAAc,IAAI,GAAG;AACtE,oBAAY,IAAI;AAAA,MAClB;AAAA,IACF;AAAA,IACA,CAAC,MAAMA,WAAU,YAAY;AAAA,EAC/B;AAGA,QAAM,cAAc;AAAA,IAClB,CAAC,MAA2C;AAC1C,gBAAU,CAAC;AAEX,WAAKA,cAAa,WAAWA,cAAa,SAAS,cAAc,IAAI,GAAG;AACtE,oBAAY,IAAI;AAAA,MAClB;AAAA,IACF;AAAA,IACA,CAAC,MAAMA,WAAU,OAAO;AAAA,EAC1B;AAGA,QAAM,cAAc;AAAA,IAClB,OAAO,MAA2C;AAChD,gBAAU,CAAC;AAGX,UAAI,EAAE,iBAAkB;AAGxB,UAAI,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,OAAQ;AAGtD,UAAI,CAAC,cAAc,IAAI,EAAG;AAG1B,UAAI,MAAM,UAAU,MAAM,WAAW,QAAS;AAG9C,QAAE,eAAe;AAGjB,sBAAgB,IAAI;AACpB,0BAAoB;AAEpB,UAAI;AAEF,cAAM,WAAW,MAAM,MAAM,MAAM;AAAA,UACjC,QAAQ;AAAA,UACR,aAAa;AAAA,UACb,SAAS;AAAA,YACP,sBAAsB;AAAA,YACtB,UAAU;AAAA,UACZ;AAAA,QACF,CAAC;AAED,YAAI,SAAS,IAAI;AACf,gBAAM,OAAO,MAAM,SAAS,KAAK;AAGjC,gBAAM,SAAS,IAAI,UAAU;AAC7B,gBAAM,MAAM,OAAO,gBAAgB,MAAM,WAAW;AAGpD,gBAAM,WAAW,IAAI,cAAc,OAAO,GAAG;AAC7C,cAAI,UAAU;AACZ,qBAAS,QAAQ;AAAA,UACnB;AAGA,gBAAM,aAAa,IAAI,cAAc,OAAO,KAAK,IAAI;AACrD,gBAAM,iBAAiB,SAAS,cAAc,OAAO,KAAK,SAAS;AAEnE,cAAI,cAAc,gBAAgB;AAChC,2BAAe,YAAY,WAAW;AAAA,UACxC;AAGA,UAAAF,UAAS,MAAM,EAAE,SAAS,OAAO,CAAC;AAAA,QACpC,OAAO;AAEL,iBAAO,SAAS,OAAO;AAAA,QACzB;AAAA,MACF,SAAS,OAAO;AAEd,eAAO,SAAS,OAAO;AAAA,MACzB,UAAE;AACA,wBAAgB,KAAK;AACrB,0BAAkB;AAAA,MACpB;AAAA,IACF;AAAA,IACA,CAAC,MAAM,SAAS,QAAQ,SAAS,mBAAmB,iBAAiB,MAAM,MAAM;AAAA,EACnF;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL;AAAA,MACA;AAAA,MACA,SAAS;AAAA,MACT,cAAc;AAAA,MACd,SAAS;AAAA,MACT,iBAAeE;AAAA,MACf,mBAAiB,gBAAgB;AAAA,MAChC,GAAG;AAAA,MAEH,yBAAe,eACd,oBACE,qBAAC,UAAK,WAAU,sBACd;AAAA,4BAAC,UAAK,WAAU,sBAAqB;AAAA,QACpC;AAAA,SACH,IAGF;AAAA;AAAA,EAEJ;AAEJ;AAoBO,SAAS,YAAY;AAC1B,SAAO;AAAA,IACL,KAAK,KAAa,SAAgC;AAChD,MAAAF,UAAS,KAAK,EAAE,SAAS,OAAO,QAAQ,SAAS,UAAU,KAAK,CAAC;AAEjE,aAAO,SAAS,OAAO;AAAA,IACzB;AAAA,IAEA,QAAQ,KAAa,SAAgC;AACnD,MAAAA,UAAS,KAAK,EAAE,SAAS,MAAM,QAAQ,SAAS,UAAU,KAAK,CAAC;AAChE,aAAO,SAAS,OAAO;AAAA,IACzB;AAAA,IAEA,OAAO;AACL,aAAO,QAAQ,KAAK;AAAA,IACtB;AAAA,IAEA,UAAU;AACR,aAAO,QAAQ,QAAQ;AAAA,IACzB;AAAA,IAEA,SAAS,KAAa;AACpB,UAAI,cAAc,GAAG,GAAG;AACtB,oBAAY,GAAG;AAAA,MACjB;AAAA,IACF;AAAA,IAEA,UAAU;AACR,aAAO,SAAS,OAAO;AAAA,IACzB;AAAA,EACF;AACF;","names":["React","React","navigate","Link","prefetch"]}