what-router 0.6.5 → 0.6.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -9,6 +9,35 @@ function isSafeUrl(url) {
9
9
  if (normalized.startsWith("vbscript:")) return false;
10
10
  return true;
11
11
  }
12
+ function safeDecodeURIComponent(value) {
13
+ try {
14
+ return decodeURIComponent(value);
15
+ } catch {
16
+ return value;
17
+ }
18
+ }
19
+ function getLinkTarget(href) {
20
+ const fallback = { href, navigateTo: href, shouldIntercept: false, isSafe: false };
21
+ if (!isSafeUrl(href)) return fallback;
22
+ if (typeof window === "undefined") {
23
+ return { href, navigateTo: href, shouldIntercept: true, isSafe: true };
24
+ }
25
+ try {
26
+ const url = new URL(href, window.location.href);
27
+ const isHttp = url.protocol === "http:" || url.protocol === "https:";
28
+ if (!isHttp || url.origin !== window.location.origin) {
29
+ return { href, navigateTo: href, shouldIntercept: false, isSafe: true };
30
+ }
31
+ return {
32
+ href,
33
+ navigateTo: url.pathname + url.search + url.hash,
34
+ shouldIntercept: true,
35
+ isSafe: true
36
+ };
37
+ } catch {
38
+ return { href, navigateTo: href, shouldIntercept: false, isSafe: true };
39
+ }
40
+ }
12
41
  var _url = signal(typeof location !== "undefined" ? location.pathname + location.search + location.hash : "/");
13
42
  var _params = signal({});
14
43
  var _query = signal({});
@@ -52,7 +81,12 @@ async function navigate(to, opts = {}) {
52
81
  const newUrl = basePath + to;
53
82
  history.replaceState(state, "", newUrl);
54
83
  _url.set(newUrl);
55
- const el = document.querySelector(to);
84
+ let el = null;
85
+ try {
86
+ el = document.getElementById(decodeURIComponent(to.slice(1)));
87
+ } catch {
88
+ el = document.getElementById(to.slice(1));
89
+ }
56
90
  if (el) el.scrollIntoView({ behavior: "smooth" });
57
91
  return;
58
92
  }
@@ -72,15 +106,22 @@ async function navigate(to, opts = {}) {
72
106
  }
73
107
  }
74
108
  _url.set(to);
75
- _isNavigating.set(false);
76
109
  };
77
- if (transition && typeof document !== "undefined" && document.startViewTransition) {
78
- try {
79
- await document.startViewTransition(doNavigation).finished;
80
- } catch (e) {
110
+ try {
111
+ if (transition && typeof document !== "undefined" && document.startViewTransition) {
112
+ try {
113
+ await document.startViewTransition(doNavigation).finished;
114
+ } catch (e) {
115
+ if (_url.peek() !== to) doNavigation();
116
+ }
117
+ } else {
118
+ doNavigation();
81
119
  }
82
- } else {
83
- doNavigation();
120
+ } catch (e) {
121
+ _navigationError.set(e);
122
+ throw e;
123
+ } finally {
124
+ _isNavigating.set(false);
84
125
  }
85
126
  }
86
127
  if (typeof window !== "undefined") {
@@ -132,7 +173,7 @@ function matchRoute(path, routes) {
132
173
  if (match) {
133
174
  const params = {};
134
175
  paramNames.forEach((name, i) => {
135
- params[name] = decodeURIComponent(match[i + 1]);
176
+ params[name] = safeDecodeURIComponent(match[i + 1]);
136
177
  });
137
178
  return { route: route2, params };
138
179
  }
@@ -146,8 +187,8 @@ function parseQuery(search) {
146
187
  for (const pair of qs.split("&")) {
147
188
  const [key, val] = pair.split("=");
148
189
  if (!key) continue;
149
- const decodedKey = decodeURIComponent(key);
150
- const decodedVal = val ? decodeURIComponent(val) : "";
190
+ const decodedKey = safeDecodeURIComponent(key);
191
+ const decodedVal = val ? safeDecodeURIComponent(val) : "";
151
192
  if (decodedKey in params) {
152
193
  if (Array.isArray(params[decodedKey])) {
153
194
  params[decodedKey].push(decodedVal);
@@ -286,15 +327,16 @@ function Link({
286
327
  transition = true,
287
328
  ...rest
288
329
  }) {
289
- const safeHref = isSafeUrl(href) ? href : "about:blank";
290
- if (!isSafeUrl(href) && typeof console !== "undefined") {
330
+ const target = getLinkTarget(href);
331
+ const safeHref = target.isSafe ? href : "about:blank";
332
+ if (!target.isSafe && typeof console !== "undefined") {
291
333
  console.warn(`[what-router] Link blocked unsafe href: ${href}`);
292
334
  }
293
- const hrefPath = safeHref.split("?")[0].split("#")[0];
335
+ const hrefPath = target.shouldIntercept ? target.navigateTo.split("?")[0].split("#")[0] : "";
294
336
  const reactiveClass = () => {
295
337
  const currentPath = route.path;
296
- const isActive = hrefPath === "/" ? currentPath === "/" : currentPath === hrefPath || currentPath.startsWith(hrefPath + "/");
297
- const isExactActive = currentPath === hrefPath;
338
+ const isActive = target.shouldIntercept && (hrefPath === "/" ? currentPath === "/" : currentPath === hrefPath || currentPath.startsWith(hrefPath + "/"));
339
+ const isExactActive = target.shouldIntercept && currentPath === hrefPath;
298
340
  return [
299
341
  cls || className,
300
342
  isActive && activeClass,
@@ -306,8 +348,9 @@ function Link({
306
348
  class: reactiveClass,
307
349
  onclick: (e) => {
308
350
  if (e.ctrlKey || e.metaKey || e.shiftKey || e.altKey || e.button !== 0) return;
351
+ if (!target.shouldIntercept) return;
309
352
  e.preventDefault();
310
- navigate(safeHref, { replace: rep, transition });
353
+ navigate(target.navigateTo, { replace: rep, transition });
311
354
  },
312
355
  onmouseenter: shouldPrefetch ? () => prefetch(safeHref) : void 0,
313
356
  ...rest
@@ -405,6 +448,12 @@ function asyncGuard(check, options = {}) {
405
448
  var prefetchedUrls = /* @__PURE__ */ new Set();
406
449
  function prefetch(href) {
407
450
  if (typeof document === "undefined") return;
451
+ if (!isSafeUrl(href)) {
452
+ if (typeof console !== "undefined") {
453
+ console.warn(`[what-router] Blocked prefetch for unsafe URL: ${href}`);
454
+ }
455
+ return;
456
+ }
408
457
  if (prefetchedUrls.has(href)) return;
409
458
  prefetchedUrls.add(href);
410
459
  const link = document.createElement("link");
package/dist/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/index.js"],
4
- "sourcesContent": ["// What Framework - Router\n// Production-grade file-based routing with nested layouts, loading states,\n// route groups, view transitions, and middleware.\n\nimport { signal, effect, computed, batch, h, ErrorBoundary } from 'what-core';\n\n// --- URL Sanitization ---\n// Rejects javascript:, data:, vbscript: protocols (case-insensitive, trimmed).\n\nexport function isSafeUrl(url) {\n if (typeof url !== 'string') return false;\n const trimmed = url.trim();\n // Check for dangerous protocols (case-insensitive, ignoring whitespace/control chars)\n const normalized = trimmed.replace(/[\\s\\x00-\\x1f]/g, '').toLowerCase();\n if (normalized.startsWith('javascript:')) return false;\n if (normalized.startsWith('data:')) return false;\n if (normalized.startsWith('vbscript:')) return false;\n return true;\n}\n\n// --- Route State (global singleton) ---\n\nconst _url = signal(typeof location !== 'undefined' ? location.pathname + location.search + location.hash : '/');\nconst _params = signal({});\nconst _query = signal({});\nconst _isNavigating = signal(false);\nconst _navigationError = signal(null);\n\nexport const route = {\n get url() { return _url(); },\n get path() { return _url().split('?')[0].split('#')[0]; },\n get params() { return _params(); },\n get query() { return _query(); },\n get hash() {\n const h = _url().split('#')[1];\n return h ? '#' + h : '';\n },\n get isNavigating() { return _isNavigating(); },\n get error() { return _navigationError(); },\n};\n\n// --- Navigation with View Transitions ---\n\nexport async function navigate(to, opts = {}) {\n const { replace = false, state = null, transition = true, _fromPopstate = false } = opts;\n\n // Reject unsafe URLs\n if (!isSafeUrl(to)) {\n if (typeof console !== 'undefined') {\n console.warn(`[what-router] Blocked navigation to unsafe URL: ${to}`);\n }\n return;\n }\n\n // Handle same-page hash links \u2014 use replaceState and scroll directly\n if (typeof window !== 'undefined' && to.startsWith('#')) {\n const currentUrl = _url();\n const basePath = currentUrl.split('#')[0];\n const newUrl = basePath + to;\n history.replaceState(state, '', newUrl);\n _url.set(newUrl);\n const el = document.querySelector(to);\n if (el) el.scrollIntoView({ behavior: 'smooth' });\n return;\n }\n\n // Don't navigate if already on the same URL\n if (to === _url()) return;\n\n // Prevent concurrent navigations \u2014 wait for current to finish\n if (_isNavigating.peek()) return;\n\n _isNavigating.set(true);\n _navigationError.set(null);\n\n const doNavigation = () => {\n // Skip history manipulation on popstate (browser already updated the URL)\n if (!_fromPopstate) {\n // Save scroll position for current URL before navigating away\n if (typeof window !== 'undefined') {\n scrollPositions.set(_url(), { x: scrollX, y: scrollY });\n }\n if (replace) {\n history.replaceState(state, '', to);\n } else {\n history.pushState(state, '', to);\n }\n }\n _url.set(to);\n _isNavigating.set(false);\n };\n\n // Use View Transitions API if available and enabled\n if (transition && typeof document !== 'undefined' && document.startViewTransition) {\n try {\n await document.startViewTransition(doNavigation).finished;\n } catch (e) {\n // Transition failed, navigation still happened\n }\n } else {\n doNavigation();\n }\n}\n\n// Back/forward support \u2014 route through navigate() so middleware runs\nif (typeof window !== 'undefined') {\n window.addEventListener('popstate', () => {\n // Save scroll position for the URL we're leaving\n scrollPositions.set(_url(), { x: scrollX, y: scrollY });\n\n const newUrl = location.pathname + location.search + location.hash;\n // Use _fromPopstate flag so navigate() skips pushState (browser already updated URL)\n navigate(newUrl, { replace: true, _fromPopstate: true, transition: false }).then(() => {\n // Restore saved scroll position for the URL we're arriving at\n const saved = scrollPositions.get(newUrl);\n if (saved) {\n requestAnimationFrame(() => window.scrollTo(saved.x, saved.y));\n }\n });\n });\n}\n\n// --- Route Matching ---\n\nfunction compilePath(path) {\n // /users/:id -> regex + param names\n // /posts/* -> catch-all\n // /[slug] -> dynamic (file-based syntax)\n // (group) -> route group (ignored in URL)\n\n // Remove route groups from path (they don't affect URL matching)\n const normalized = path\n .replace(/\\([\\w-]+\\)\\//g, '') // Remove (group)/ prefixes\n .replace(/\\[\\.\\.\\.(\\w+)\\]/g, (_, name) => `*:${name}`) // Preserve catch-all name\n .replace(/\\[(\\w+)\\]/g, ':$1'); // File-based [param] to :param\n\n const paramNames = [];\n let catchAll = null;\n\n const regexStr = normalized\n .split('/')\n .map(segment => {\n if (segment.startsWith('*:')) {\n catchAll = segment.slice(2);\n paramNames.push(catchAll);\n return '(.+)';\n }\n if (segment === '*') {\n catchAll = 'rest';\n paramNames.push('rest');\n return '(.+)';\n }\n if (segment.startsWith(':')) {\n paramNames.push(segment.slice(1));\n return '([^/]+)';\n }\n return segment.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n })\n .join('/');\n\n const regex = new RegExp(`^${regexStr}$`);\n return { regex, paramNames, catchAll };\n}\n\nfunction matchRoute(path, routes) {\n // Filter out routes without a path (layout-only routes, etc.)\n const routable = routes.filter(r => r.path);\n\n // Sort routes by specificity (more specific first)\n const sorted = routable.sort((a, b) => {\n const aSpecific = (a.path.match(/:/g) || []).length + (a.path.includes('*') ? 100 : 0);\n const bSpecific = (b.path.match(/:/g) || []).length + (b.path.includes('*') ? 100 : 0);\n return aSpecific - bSpecific;\n });\n\n for (const route of sorted) {\n const { regex, paramNames } = compilePath(route.path);\n const match = path.match(regex);\n if (match) {\n const params = {};\n paramNames.forEach((name, i) => {\n params[name] = decodeURIComponent(match[i + 1]);\n });\n return { route, params };\n }\n }\n return null;\n}\n\nfunction parseQuery(search) {\n const params = {};\n if (!search) return params;\n const qs = search.startsWith('?') ? search.slice(1) : search;\n for (const pair of qs.split('&')) {\n const [key, val] = pair.split('=');\n if (!key) continue;\n const decodedKey = decodeURIComponent(key);\n const decodedVal = val ? decodeURIComponent(val) : '';\n if (decodedKey in params) {\n // Collect repeated keys into arrays\n if (Array.isArray(params[decodedKey])) {\n params[decodedKey].push(decodedVal);\n } else {\n params[decodedKey] = [params[decodedKey], decodedVal];\n }\n } else {\n params[decodedKey] = decodedVal;\n }\n }\n return params;\n}\n\n// --- Nested Layouts ---\n\n// Build the layout chain for a route\nfunction buildLayoutChain(route, routes) {\n const layouts = [];\n if (!route.path) return layouts;\n\n // Check for nested layouts based on path segments\n const segments = route.path.split('/').filter(Boolean);\n let currentPath = '';\n\n for (const segment of segments) {\n currentPath += '/' + segment;\n\n // Find layout for this path level\n const layoutRoute = routes.find(r =>\n r.layout && r.path === currentPath + '/_layout'\n );\n if (layoutRoute) {\n layouts.push(layoutRoute.layout);\n }\n }\n\n // Add route's own layout if specified\n if (route.layout) {\n layouts.push(route.layout);\n }\n\n return layouts;\n}\n\n// --- Middleware redirect loop detection ---\nconst _redirectHistory = [];\nconst MAX_REDIRECTS = 10;\n\n// --- Router Component ---\n\nexport function Router({ routes, fallback, globalLayout }) {\n // Return a reactive function child. The Router component runs ONCE,\n // but the returned function re-evaluates whenever _url changes,\n // and the fine-grained runtime updates the DOM accordingly.\n return () => {\n const currentUrl = _url();\n const path = currentUrl.split('?')[0].split('#')[0];\n const search = currentUrl.split('?')[1]?.split('#')[0] || '';\n const isNavigating = _isNavigating();\n\n const matched = matchRoute(path, routes);\n\n if (matched) {\n batch(() => {\n _params.set(matched.params);\n _query.set(parseQuery(search));\n });\n\n const { route: r, params } = matched;\n const queryObj = parseQuery(search);\n\n // Run middleware (sync only \u2014 async middleware should use asyncGuard)\n if (r.middleware && r.middleware.length > 0) {\n for (const mw of r.middleware) {\n const result = mw({ path, params, query: queryObj, route: r });\n if (result === false) {\n // Middleware rejected \u2014 show fallback\n if (fallback) return h(fallback, {});\n return h('div', { class: 'what-403' }, h('h1', null, '403'), h('p', null, 'Access denied'));\n }\n if (typeof result === 'string') {\n // Redirect loop detection\n _redirectHistory.push(result);\n if (_redirectHistory.length > MAX_REDIRECTS) {\n const cycle = _redirectHistory.slice(-5).join(' \u2192 ');\n _redirectHistory.length = 0;\n console.error(`[what-router] Redirect loop detected: ${cycle}`);\n _isNavigating.set(false);\n return h('div', { class: 'what-redirect-loop' },\n h('h1', null, 'Redirect Loop'),\n h('p', null, 'Too many redirects. Check your middleware configuration.')\n );\n }\n // Check for direct cycle (A \u2192 B \u2192 A)\n const seen = new Set();\n let hasCycle = false;\n for (const url of _redirectHistory) {\n if (seen.has(url)) { hasCycle = true; break; }\n seen.add(url);\n }\n if (hasCycle) {\n const cycle = _redirectHistory.join(' \u2192 ');\n _redirectHistory.length = 0;\n console.error(`[what-router] Redirect cycle detected: ${cycle}`);\n _isNavigating.set(false);\n return h('div', { class: 'what-redirect-loop' },\n h('h1', null, 'Redirect Loop'),\n h('p', null, 'Circular redirect detected. Check your middleware configuration.')\n );\n }\n // Middleware returned a redirect path\n navigate(result, { replace: true });\n return null;\n }\n }\n }\n // Successful render \u2014 clear redirect history\n _redirectHistory.length = 0;\n\n // Build element with loading state support\n let element;\n\n if (r.loading && isNavigating) {\n element = h(r.loading, {});\n } else {\n element = h(r.component, {\n params,\n query: queryObj,\n route: r,\n });\n }\n\n // Wrap with per-route error boundary if specified\n if (r.error) {\n element = h(ErrorBoundary, { fallback: r.error }, element);\n }\n\n // Wrap with nested layouts (innermost to outermost)\n const layouts = buildLayoutChain(r, routes);\n for (const Layout of layouts.reverse()) {\n element = h(Layout, { params, query: queryObj }, element);\n }\n\n // Global layout wrapper\n if (globalLayout) {\n element = h(globalLayout, {}, element);\n }\n\n return element;\n }\n\n // 404\n if (fallback) return h(fallback, {});\n return h('div', { class: 'what-404' },\n h('h1', null, '404'),\n h('p', null, 'Page not found')\n );\n };\n}\n\n// --- Link Component ---\n\nexport function Link({\n href,\n class: cls,\n className,\n children,\n replace: rep,\n prefetch: shouldPrefetch = true,\n activeClass = 'active',\n exactActiveClass = 'exact-active',\n transition = true,\n ...rest\n}) {\n // Sanitize href \u2014 reject dangerous protocols\n const safeHref = isSafeUrl(href) ? href : 'about:blank';\n if (!isSafeUrl(href) && typeof console !== 'undefined') {\n console.warn(`[what-router] Link blocked unsafe href: ${href}`);\n }\n\n // Strip query string and hash from href for path comparison\n const hrefPath = safeHref.split('?')[0].split('#')[0];\n\n // Use a reactive function for class so active states update on navigation.\n // In the run-once model, reading route.path directly would snapshot it.\n const reactiveClass = () => {\n const currentPath = route.path;\n const isActive = hrefPath === '/'\n ? currentPath === '/'\n : currentPath === hrefPath || currentPath.startsWith(hrefPath + '/');\n const isExactActive = currentPath === hrefPath;\n\n return [\n cls || className,\n isActive && activeClass,\n isExactActive && exactActiveClass,\n ].filter(Boolean).join(' ') || undefined;\n };\n\n return h('a', {\n href: safeHref,\n class: reactiveClass,\n onclick: (e) => {\n // Only intercept left-clicks without modifiers\n if (e.ctrlKey || e.metaKey || e.shiftKey || e.altKey || e.button !== 0) return;\n e.preventDefault();\n navigate(safeHref, { replace: rep, transition });\n },\n onmouseenter: shouldPrefetch ? () => prefetch(safeHref) : undefined,\n ...rest,\n }, ...(Array.isArray(children) ? children : [children]));\n}\n\n// --- NavLink with active states ---\n\nexport function NavLink(props) {\n return Link(props);\n}\n\n// --- Define Routes Helper ---\n// Creates route config from a flat object for convenience.\n\nexport function defineRoutes(config) {\n return Object.entries(config).map(([path, value]) => {\n if (typeof value === 'function') {\n return { path, component: value };\n }\n // Object form with layout, middleware, loading, error, etc.\n return { path, ...value };\n });\n}\n\n// --- Nested Route Helper ---\n\nexport function nestedRoutes(basePath, children, options = {}) {\n const { layout, loading, error } = options;\n\n return children.map(child => ({\n ...child,\n path: basePath + child.path,\n layout: child.layout || layout,\n loading: child.loading || loading,\n error: child.error || error,\n }));\n}\n\n// --- Route Groups ---\n// Group routes without affecting URL structure\n\nexport function routeGroup(name, routes, options = {}) {\n const { layout, middleware } = options;\n\n return routes.map(route => ({\n ...route,\n _group: name,\n layout: route.layout || layout,\n middleware: [...(route.middleware || []), ...(middleware || [])],\n }));\n}\n\n// --- Redirect ---\n\nexport function Redirect({ to }) {\n navigate(to, { replace: true });\n return null;\n}\n\n// --- Route Guards / Middleware ---\n\nexport function guard(check, fallback) {\n return (Component) => {\n return function GuardedRoute(props) {\n const result = check(props);\n\n // Support async guards\n if (result instanceof Promise) {\n // Return loading while checking\n return h('div', { class: 'what-guard-loading' }, 'Loading...');\n }\n\n if (result) {\n return h(Component, props);\n }\n\n if (typeof fallback === 'string') {\n navigate(fallback, { replace: true });\n return null;\n }\n return h(fallback, props);\n };\n };\n}\n\n// Async guard with suspense\nexport function asyncGuard(check, options = {}) {\n const { fallback = '/login', loading = null } = options;\n\n return (Component) => {\n return function AsyncGuardedRoute(props) {\n const status = signal('pending');\n const checkResult = signal(null);\n let cancelled = false;\n\n effect(() => {\n cancelled = false;\n Promise.resolve(check(props))\n .then(result => {\n if (cancelled) return;\n checkResult.set(result);\n status.set(result ? 'allowed' : 'denied');\n })\n .catch(() => {\n if (!cancelled) status.set('denied');\n });\n return () => { cancelled = true; };\n });\n\n // Return a reactive function child so status changes update the DOM.\n // Components run once, so reading status() outside a reactive wrapper\n // would snapshot the value and never update.\n return () => {\n const currentStatus = status();\n\n if (currentStatus === 'pending') {\n return loading ? h(loading, {}) : null;\n }\n\n if (currentStatus === 'allowed') {\n return h(Component, props);\n }\n\n if (typeof fallback === 'string') {\n navigate(fallback, { replace: true });\n return null;\n }\n return h(fallback, props);\n };\n };\n };\n}\n\n// --- Prefetch ---\n// Hint the browser to prefetch a route's assets.\n\nconst prefetchedUrls = new Set();\n\nexport function prefetch(href) {\n if (typeof document === 'undefined') return;\n if (prefetchedUrls.has(href)) return;\n prefetchedUrls.add(href);\n\n const link = document.createElement('link');\n link.rel = 'prefetch';\n link.href = href;\n document.head.appendChild(link);\n}\n\n// --- Scroll Restoration ---\n\nconst scrollPositions = new Map();\n\nexport function enableScrollRestoration() {\n if (typeof window === 'undefined') return;\n\n // Save scroll position before navigation\n window.addEventListener('beforeunload', () => {\n scrollPositions.set(location.pathname, window.scrollY);\n });\n\n // Restore scroll position after navigation\n effect(() => {\n const path = route.path;\n const savedPosition = scrollPositions.get(path);\n\n requestAnimationFrame(() => {\n if (savedPosition !== undefined) {\n window.scrollTo(0, savedPosition);\n } else if (route.hash) {\n const el = document.querySelector(route.hash);\n el?.scrollIntoView();\n } else {\n window.scrollTo(0, 0);\n }\n });\n });\n}\n\n// --- View Transition Helpers ---\n\nexport function viewTransitionName(name) {\n return { style: { viewTransitionName: name } };\n}\n\n// Configure view transition types\nexport function setViewTransition(type) {\n if (typeof document === 'undefined') return;\n document.documentElement.dataset.transition = type;\n}\n\n// --- useRoute Hook ---\n\nexport function useRoute() {\n return {\n path: computed(() => route.path),\n params: computed(() => route.params),\n query: computed(() => route.query),\n hash: computed(() => route.hash),\n isNavigating: computed(() => route.isNavigating),\n navigate,\n prefetch,\n };\n}\n\n// --- Outlet Component ---\n// For nested route rendering\n\nexport function Outlet({ children }) {\n // Children passed from parent layout\n return children || null;\n}\n\n// --- File-Based Router ---\n// Consumes routes generated by what-compiler's file router (virtual:what-routes).\n// Usage:\n// import { routes } from 'virtual:what-routes';\n// mount(<FileRouter routes={routes} />, '#app');\n\nexport function FileRouter({\n routes,\n layout: globalLayout,\n fallback,\n error: globalError,\n}) {\n // Convert file-router route format to Router's expected format\n const routerRoutes = routes.map(r => ({\n path: r.path,\n component: r.component,\n layout: r.layout || undefined,\n // Attach page mode as metadata for build system\n _mode: r.mode || 'client',\n }));\n\n // Router already returns a reactive function child \u2014 just delegate\n return Router({\n routes: routerRoutes,\n globalLayout,\n fallback: fallback || Default404,\n });\n}\n\nfunction Default404() {\n return h('div', { style: 'text-align:center;padding:60px 20px' },\n h('h1', { style: 'font-size:48px;margin-bottom:8px' }, '404'),\n h('p', { style: 'color:#64748b' }, 'Page not found'),\n );\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["// What Framework - Router\n// Production-grade file-based routing with nested layouts, loading states,\n// route groups, view transitions, and middleware.\n\nimport { signal, effect, computed, batch, h, ErrorBoundary } from 'what-core';\n\n// --- URL Sanitization ---\n// Rejects javascript:, data:, vbscript: protocols (case-insensitive, trimmed).\n\nexport function isSafeUrl(url) {\n if (typeof url !== 'string') return false;\n const trimmed = url.trim();\n // Check for dangerous protocols (case-insensitive, ignoring whitespace/control chars)\n const normalized = trimmed.replace(/[\\s\\x00-\\x1f]/g, '').toLowerCase();\n if (normalized.startsWith('javascript:')) return false;\n if (normalized.startsWith('data:')) return false;\n if (normalized.startsWith('vbscript:')) return false;\n return true;\n}\n\nfunction safeDecodeURIComponent(value) {\n try {\n return decodeURIComponent(value);\n } catch {\n return value;\n }\n}\n\nfunction getLinkTarget(href) {\n const fallback = { href, navigateTo: href, shouldIntercept: false, isSafe: false };\n if (!isSafeUrl(href)) return fallback;\n\n if (typeof window === 'undefined') {\n return { href, navigateTo: href, shouldIntercept: true, isSafe: true };\n }\n\n try {\n const url = new URL(href, window.location.href);\n const isHttp = url.protocol === 'http:' || url.protocol === 'https:';\n if (!isHttp || url.origin !== window.location.origin) {\n return { href, navigateTo: href, shouldIntercept: false, isSafe: true };\n }\n return {\n href,\n navigateTo: url.pathname + url.search + url.hash,\n shouldIntercept: true,\n isSafe: true,\n };\n } catch {\n return { href, navigateTo: href, shouldIntercept: false, isSafe: true };\n }\n}\n\n// --- Route State (global singleton) ---\n\nconst _url = signal(typeof location !== 'undefined' ? location.pathname + location.search + location.hash : '/');\nconst _params = signal({});\nconst _query = signal({});\nconst _isNavigating = signal(false);\nconst _navigationError = signal(null);\n\nexport const route = {\n get url() { return _url(); },\n get path() { return _url().split('?')[0].split('#')[0]; },\n get params() { return _params(); },\n get query() { return _query(); },\n get hash() {\n const h = _url().split('#')[1];\n return h ? '#' + h : '';\n },\n get isNavigating() { return _isNavigating(); },\n get error() { return _navigationError(); },\n};\n\n// --- Navigation with View Transitions ---\n\nexport async function navigate(to, opts = {}) {\n const { replace = false, state = null, transition = true, _fromPopstate = false } = opts;\n\n // Reject unsafe URLs\n if (!isSafeUrl(to)) {\n if (typeof console !== 'undefined') {\n console.warn(`[what-router] Blocked navigation to unsafe URL: ${to}`);\n }\n return;\n }\n\n // Handle same-page hash links \u2014 use replaceState and scroll directly\n if (typeof window !== 'undefined' && to.startsWith('#')) {\n const currentUrl = _url();\n const basePath = currentUrl.split('#')[0];\n const newUrl = basePath + to;\n history.replaceState(state, '', newUrl);\n _url.set(newUrl);\n let el = null;\n try {\n el = document.getElementById(decodeURIComponent(to.slice(1)));\n } catch {\n el = document.getElementById(to.slice(1));\n }\n if (el) el.scrollIntoView({ behavior: 'smooth' });\n return;\n }\n\n // Don't navigate if already on the same URL\n if (to === _url()) return;\n\n // Prevent concurrent navigations \u2014 wait for current to finish\n if (_isNavigating.peek()) return;\n\n _isNavigating.set(true);\n _navigationError.set(null);\n\n const doNavigation = () => {\n // Skip history manipulation on popstate (browser already updated the URL)\n if (!_fromPopstate) {\n // Save scroll position for current URL before navigating away\n if (typeof window !== 'undefined') {\n scrollPositions.set(_url(), { x: scrollX, y: scrollY });\n }\n if (replace) {\n history.replaceState(state, '', to);\n } else {\n history.pushState(state, '', to);\n }\n }\n _url.set(to);\n };\n\n try {\n // Use View Transitions API if available and enabled\n if (transition && typeof document !== 'undefined' && document.startViewTransition) {\n try {\n await document.startViewTransition(doNavigation).finished;\n } catch (e) {\n // Transition failed; fall back to direct navigation if it did not run.\n if (_url.peek() !== to) doNavigation();\n }\n } else {\n doNavigation();\n }\n } catch (e) {\n _navigationError.set(e);\n throw e;\n } finally {\n _isNavigating.set(false);\n }\n}\n\n// Back/forward support \u2014 route through navigate() so middleware runs\nif (typeof window !== 'undefined') {\n window.addEventListener('popstate', () => {\n // Save scroll position for the URL we're leaving\n scrollPositions.set(_url(), { x: scrollX, y: scrollY });\n\n const newUrl = location.pathname + location.search + location.hash;\n // Use _fromPopstate flag so navigate() skips pushState (browser already updated URL)\n navigate(newUrl, { replace: true, _fromPopstate: true, transition: false }).then(() => {\n // Restore saved scroll position for the URL we're arriving at\n const saved = scrollPositions.get(newUrl);\n if (saved) {\n requestAnimationFrame(() => window.scrollTo(saved.x, saved.y));\n }\n });\n });\n}\n\n// --- Route Matching ---\n\nfunction compilePath(path) {\n // /users/:id -> regex + param names\n // /posts/* -> catch-all\n // /[slug] -> dynamic (file-based syntax)\n // (group) -> route group (ignored in URL)\n\n // Remove route groups from path (they don't affect URL matching)\n const normalized = path\n .replace(/\\([\\w-]+\\)\\//g, '') // Remove (group)/ prefixes\n .replace(/\\[\\.\\.\\.(\\w+)\\]/g, (_, name) => `*:${name}`) // Preserve catch-all name\n .replace(/\\[(\\w+)\\]/g, ':$1'); // File-based [param] to :param\n\n const paramNames = [];\n let catchAll = null;\n\n const regexStr = normalized\n .split('/')\n .map(segment => {\n if (segment.startsWith('*:')) {\n catchAll = segment.slice(2);\n paramNames.push(catchAll);\n return '(.+)';\n }\n if (segment === '*') {\n catchAll = 'rest';\n paramNames.push('rest');\n return '(.+)';\n }\n if (segment.startsWith(':')) {\n paramNames.push(segment.slice(1));\n return '([^/]+)';\n }\n return segment.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n })\n .join('/');\n\n const regex = new RegExp(`^${regexStr}$`);\n return { regex, paramNames, catchAll };\n}\n\nfunction matchRoute(path, routes) {\n // Filter out routes without a path (layout-only routes, etc.)\n const routable = routes.filter(r => r.path);\n\n // Sort routes by specificity (more specific first)\n const sorted = routable.sort((a, b) => {\n const aSpecific = (a.path.match(/:/g) || []).length + (a.path.includes('*') ? 100 : 0);\n const bSpecific = (b.path.match(/:/g) || []).length + (b.path.includes('*') ? 100 : 0);\n return aSpecific - bSpecific;\n });\n\n for (const route of sorted) {\n const { regex, paramNames } = compilePath(route.path);\n const match = path.match(regex);\n if (match) {\n const params = {};\n paramNames.forEach((name, i) => {\n params[name] = safeDecodeURIComponent(match[i + 1]);\n });\n return { route, params };\n }\n }\n return null;\n}\n\nfunction parseQuery(search) {\n const params = {};\n if (!search) return params;\n const qs = search.startsWith('?') ? search.slice(1) : search;\n for (const pair of qs.split('&')) {\n const [key, val] = pair.split('=');\n if (!key) continue;\n const decodedKey = safeDecodeURIComponent(key);\n const decodedVal = val ? safeDecodeURIComponent(val) : '';\n if (decodedKey in params) {\n // Collect repeated keys into arrays\n if (Array.isArray(params[decodedKey])) {\n params[decodedKey].push(decodedVal);\n } else {\n params[decodedKey] = [params[decodedKey], decodedVal];\n }\n } else {\n params[decodedKey] = decodedVal;\n }\n }\n return params;\n}\n\n// --- Nested Layouts ---\n\n// Build the layout chain for a route\nfunction buildLayoutChain(route, routes) {\n const layouts = [];\n if (!route.path) return layouts;\n\n // Check for nested layouts based on path segments\n const segments = route.path.split('/').filter(Boolean);\n let currentPath = '';\n\n for (const segment of segments) {\n currentPath += '/' + segment;\n\n // Find layout for this path level\n const layoutRoute = routes.find(r =>\n r.layout && r.path === currentPath + '/_layout'\n );\n if (layoutRoute) {\n layouts.push(layoutRoute.layout);\n }\n }\n\n // Add route's own layout if specified\n if (route.layout) {\n layouts.push(route.layout);\n }\n\n return layouts;\n}\n\n// --- Middleware redirect loop detection ---\nconst _redirectHistory = [];\nconst MAX_REDIRECTS = 10;\n\n// --- Router Component ---\n\nexport function Router({ routes, fallback, globalLayout }) {\n // Return a reactive function child. The Router component runs ONCE,\n // but the returned function re-evaluates whenever _url changes,\n // and the fine-grained runtime updates the DOM accordingly.\n return () => {\n const currentUrl = _url();\n const path = currentUrl.split('?')[0].split('#')[0];\n const search = currentUrl.split('?')[1]?.split('#')[0] || '';\n const isNavigating = _isNavigating();\n\n const matched = matchRoute(path, routes);\n\n if (matched) {\n batch(() => {\n _params.set(matched.params);\n _query.set(parseQuery(search));\n });\n\n const { route: r, params } = matched;\n const queryObj = parseQuery(search);\n\n // Run middleware (sync only \u2014 async middleware should use asyncGuard)\n if (r.middleware && r.middleware.length > 0) {\n for (const mw of r.middleware) {\n const result = mw({ path, params, query: queryObj, route: r });\n if (result === false) {\n // Middleware rejected \u2014 show fallback\n if (fallback) return h(fallback, {});\n return h('div', { class: 'what-403' }, h('h1', null, '403'), h('p', null, 'Access denied'));\n }\n if (typeof result === 'string') {\n // Redirect loop detection\n _redirectHistory.push(result);\n if (_redirectHistory.length > MAX_REDIRECTS) {\n const cycle = _redirectHistory.slice(-5).join(' \u2192 ');\n _redirectHistory.length = 0;\n console.error(`[what-router] Redirect loop detected: ${cycle}`);\n _isNavigating.set(false);\n return h('div', { class: 'what-redirect-loop' },\n h('h1', null, 'Redirect Loop'),\n h('p', null, 'Too many redirects. Check your middleware configuration.')\n );\n }\n // Check for direct cycle (A \u2192 B \u2192 A)\n const seen = new Set();\n let hasCycle = false;\n for (const url of _redirectHistory) {\n if (seen.has(url)) { hasCycle = true; break; }\n seen.add(url);\n }\n if (hasCycle) {\n const cycle = _redirectHistory.join(' \u2192 ');\n _redirectHistory.length = 0;\n console.error(`[what-router] Redirect cycle detected: ${cycle}`);\n _isNavigating.set(false);\n return h('div', { class: 'what-redirect-loop' },\n h('h1', null, 'Redirect Loop'),\n h('p', null, 'Circular redirect detected. Check your middleware configuration.')\n );\n }\n // Middleware returned a redirect path\n navigate(result, { replace: true });\n return null;\n }\n }\n }\n // Successful render \u2014 clear redirect history\n _redirectHistory.length = 0;\n\n // Build element with loading state support\n let element;\n\n if (r.loading && isNavigating) {\n element = h(r.loading, {});\n } else {\n element = h(r.component, {\n params,\n query: queryObj,\n route: r,\n });\n }\n\n // Wrap with per-route error boundary if specified\n if (r.error) {\n element = h(ErrorBoundary, { fallback: r.error }, element);\n }\n\n // Wrap with nested layouts (innermost to outermost)\n const layouts = buildLayoutChain(r, routes);\n for (const Layout of layouts.reverse()) {\n element = h(Layout, { params, query: queryObj }, element);\n }\n\n // Global layout wrapper\n if (globalLayout) {\n element = h(globalLayout, {}, element);\n }\n\n return element;\n }\n\n // 404\n if (fallback) return h(fallback, {});\n return h('div', { class: 'what-404' },\n h('h1', null, '404'),\n h('p', null, 'Page not found')\n );\n };\n}\n\n// --- Link Component ---\n\nexport function Link({\n href,\n class: cls,\n className,\n children,\n replace: rep,\n prefetch: shouldPrefetch = true,\n activeClass = 'active',\n exactActiveClass = 'exact-active',\n transition = true,\n ...rest\n}) {\n // Sanitize href \u2014 reject dangerous protocols and only intercept same-origin routes.\n const target = getLinkTarget(href);\n const safeHref = target.isSafe ? href : 'about:blank';\n if (!target.isSafe && typeof console !== 'undefined') {\n console.warn(`[what-router] Link blocked unsafe href: ${href}`);\n }\n\n // Strip query string and hash from same-origin targets for path comparison.\n const hrefPath = target.shouldIntercept\n ? target.navigateTo.split('?')[0].split('#')[0]\n : '';\n\n // Use a reactive function for class so active states update on navigation.\n // In the run-once model, reading route.path directly would snapshot it.\n const reactiveClass = () => {\n const currentPath = route.path;\n const isActive = target.shouldIntercept && (hrefPath === '/'\n ? currentPath === '/'\n : currentPath === hrefPath || currentPath.startsWith(hrefPath + '/'));\n const isExactActive = target.shouldIntercept && currentPath === hrefPath;\n\n return [\n cls || className,\n isActive && activeClass,\n isExactActive && exactActiveClass,\n ].filter(Boolean).join(' ') || undefined;\n };\n\n return h('a', {\n href: safeHref,\n class: reactiveClass,\n onclick: (e) => {\n // Only intercept left-clicks without modifiers\n if (e.ctrlKey || e.metaKey || e.shiftKey || e.altKey || e.button !== 0) return;\n if (!target.shouldIntercept) return;\n e.preventDefault();\n navigate(target.navigateTo, { replace: rep, transition });\n },\n onmouseenter: shouldPrefetch ? () => prefetch(safeHref) : undefined,\n ...rest,\n }, ...(Array.isArray(children) ? children : [children]));\n}\n\n// --- NavLink with active states ---\n\nexport function NavLink(props) {\n return Link(props);\n}\n\n// --- Define Routes Helper ---\n// Creates route config from a flat object for convenience.\n\nexport function defineRoutes(config) {\n return Object.entries(config).map(([path, value]) => {\n if (typeof value === 'function') {\n return { path, component: value };\n }\n // Object form with layout, middleware, loading, error, etc.\n return { path, ...value };\n });\n}\n\n// --- Nested Route Helper ---\n\nexport function nestedRoutes(basePath, children, options = {}) {\n const { layout, loading, error } = options;\n\n return children.map(child => ({\n ...child,\n path: basePath + child.path,\n layout: child.layout || layout,\n loading: child.loading || loading,\n error: child.error || error,\n }));\n}\n\n// --- Route Groups ---\n// Group routes without affecting URL structure\n\nexport function routeGroup(name, routes, options = {}) {\n const { layout, middleware } = options;\n\n return routes.map(route => ({\n ...route,\n _group: name,\n layout: route.layout || layout,\n middleware: [...(route.middleware || []), ...(middleware || [])],\n }));\n}\n\n// --- Redirect ---\n\nexport function Redirect({ to }) {\n navigate(to, { replace: true });\n return null;\n}\n\n// --- Route Guards / Middleware ---\n\nexport function guard(check, fallback) {\n return (Component) => {\n return function GuardedRoute(props) {\n const result = check(props);\n\n // Support async guards\n if (result instanceof Promise) {\n // Return loading while checking\n return h('div', { class: 'what-guard-loading' }, 'Loading...');\n }\n\n if (result) {\n return h(Component, props);\n }\n\n if (typeof fallback === 'string') {\n navigate(fallback, { replace: true });\n return null;\n }\n return h(fallback, props);\n };\n };\n}\n\n// Async guard with suspense\nexport function asyncGuard(check, options = {}) {\n const { fallback = '/login', loading = null } = options;\n\n return (Component) => {\n return function AsyncGuardedRoute(props) {\n const status = signal('pending');\n const checkResult = signal(null);\n let cancelled = false;\n\n effect(() => {\n cancelled = false;\n Promise.resolve(check(props))\n .then(result => {\n if (cancelled) return;\n checkResult.set(result);\n status.set(result ? 'allowed' : 'denied');\n })\n .catch(() => {\n if (!cancelled) status.set('denied');\n });\n return () => { cancelled = true; };\n });\n\n // Return a reactive function child so status changes update the DOM.\n // Components run once, so reading status() outside a reactive wrapper\n // would snapshot the value and never update.\n return () => {\n const currentStatus = status();\n\n if (currentStatus === 'pending') {\n return loading ? h(loading, {}) : null;\n }\n\n if (currentStatus === 'allowed') {\n return h(Component, props);\n }\n\n if (typeof fallback === 'string') {\n navigate(fallback, { replace: true });\n return null;\n }\n return h(fallback, props);\n };\n };\n };\n}\n\n// --- Prefetch ---\n// Hint the browser to prefetch a route's assets.\n\nconst prefetchedUrls = new Set();\n\nexport function prefetch(href) {\n if (typeof document === 'undefined') return;\n if (!isSafeUrl(href)) {\n if (typeof console !== 'undefined') {\n console.warn(`[what-router] Blocked prefetch for unsafe URL: ${href}`);\n }\n return;\n }\n if (prefetchedUrls.has(href)) return;\n prefetchedUrls.add(href);\n\n const link = document.createElement('link');\n link.rel = 'prefetch';\n link.href = href;\n document.head.appendChild(link);\n}\n\n// --- Scroll Restoration ---\n\nconst scrollPositions = new Map();\n\nexport function enableScrollRestoration() {\n if (typeof window === 'undefined') return;\n\n // Save scroll position before navigation\n window.addEventListener('beforeunload', () => {\n scrollPositions.set(location.pathname, window.scrollY);\n });\n\n // Restore scroll position after navigation\n effect(() => {\n const path = route.path;\n const savedPosition = scrollPositions.get(path);\n\n requestAnimationFrame(() => {\n if (savedPosition !== undefined) {\n window.scrollTo(0, savedPosition);\n } else if (route.hash) {\n const el = document.querySelector(route.hash);\n el?.scrollIntoView();\n } else {\n window.scrollTo(0, 0);\n }\n });\n });\n}\n\n// --- View Transition Helpers ---\n\nexport function viewTransitionName(name) {\n return { style: { viewTransitionName: name } };\n}\n\n// Configure view transition types\nexport function setViewTransition(type) {\n if (typeof document === 'undefined') return;\n document.documentElement.dataset.transition = type;\n}\n\n// --- useRoute Hook ---\n\nexport function useRoute() {\n return {\n path: computed(() => route.path),\n params: computed(() => route.params),\n query: computed(() => route.query),\n hash: computed(() => route.hash),\n isNavigating: computed(() => route.isNavigating),\n navigate,\n prefetch,\n };\n}\n\n// --- Outlet Component ---\n// For nested route rendering\n\nexport function Outlet({ children }) {\n // Children passed from parent layout\n return children || null;\n}\n\n// --- File-Based Router ---\n// Consumes routes generated by what-compiler's file router (virtual:what-routes).\n// Usage:\n// import { routes } from 'virtual:what-routes';\n// mount(<FileRouter routes={routes} />, '#app');\n\nexport function FileRouter({\n routes,\n layout: globalLayout,\n fallback,\n error: globalError,\n}) {\n // Convert file-router route format to Router's expected format\n const routerRoutes = routes.map(r => ({\n path: r.path,\n component: r.component,\n layout: r.layout || undefined,\n // Attach page mode as metadata for build system\n _mode: r.mode || 'client',\n }));\n\n // Router already returns a reactive function child \u2014 just delegate\n return Router({\n routes: routerRoutes,\n globalLayout,\n fallback: fallback || Default404,\n });\n}\n\nfunction Default404() {\n return h('div', { style: 'text-align:center;padding:60px 20px' },\n h('h1', { style: 'font-size:48px;margin-bottom:8px' }, '404'),\n h('p', { style: 'color:#64748b' }, 'Page not found'),\n );\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["h", "route"]
7
7
  }
package/dist/index.min.js CHANGED
@@ -1,2 +1,2 @@
1
- import{signal as w,effect as _,computed as b,batch as $,h as a,ErrorBoundary as W}from"what-core";function k(e){if(typeof e!="string")return!1;let n=e.trim().replace(/[\s\x00-\x1f]/g,"").toLowerCase();return!(n.startsWith("javascript:")||n.startsWith("data:")||n.startsWith("vbscript:"))}var h=w(typeof location<"u"?location.pathname+location.search+location.hash:"/"),L=w({}),N=w({}),g=w(!1),U=w(null),m={get url(){return h()},get path(){return h().split("?")[0].split("#")[0]},get params(){return L()},get query(){return N()},get hash(){let e=h().split("#")[1];return e?"#"+e:""},get isNavigating(){return g()},get error(){return U()}};async function x(e,t={}){let{replace:n=!1,state:s=null,transition:o=!0,_fromPopstate:i=!1}=t;if(!k(e)){typeof console<"u"&&console.warn(`[what-router] Blocked navigation to unsafe URL: ${e}`);return}if(typeof window<"u"&&e.startsWith("#")){let f=h().split("#")[0]+e;history.replaceState(s,"",f),h.set(f);let l=document.querySelector(e);l&&l.scrollIntoView({behavior:"smooth"});return}if(e===h()||g.peek())return;g.set(!0),U.set(null);let r=()=>{i||(typeof window<"u"&&P.set(h(),{x:scrollX,y:scrollY}),n?history.replaceState(s,"",e):history.pushState(s,"",e)),h.set(e),g.set(!1)};if(o&&typeof document<"u"&&document.startViewTransition)try{await document.startViewTransition(r).finished}catch{}else r()}typeof window<"u"&&window.addEventListener("popstate",()=>{P.set(h(),{x:scrollX,y:scrollY});let e=location.pathname+location.search+location.hash;x(e,{replace:!0,_fromPopstate:!0,transition:!1}).then(()=>{let t=P.get(e);t&&requestAnimationFrame(()=>window.scrollTo(t.x,t.y))})});function j(e){let t=e.replace(/\([\w-]+\)\//g,"").replace(/\[\.\.\.(\w+)\]/g,(r,c)=>`*:${c}`).replace(/\[(\w+)\]/g,":$1"),n=[],s=null,o=t.split("/").map(r=>r.startsWith("*:")?(s=r.slice(2),n.push(s),"(.+)"):r==="*"?(s="rest",n.push("rest"),"(.+)"):r.startsWith(":")?(n.push(r.slice(1)),"([^/]+)"):r.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")).join("/");return{regex:new RegExp(`^${o}$`),paramNames:n,catchAll:s}}function I(e,t){let s=t.filter(o=>o.path).sort((o,i)=>{let r=(o.path.match(/:/g)||[]).length+(o.path.includes("*")?100:0),c=(i.path.match(/:/g)||[]).length+(i.path.includes("*")?100:0);return r-c});for(let o of s){let{regex:i,paramNames:r}=j(o.path),c=e.match(i);if(c){let u={};return r.forEach((f,l)=>{u[f]=decodeURIComponent(c[l+1])}),{route:o,params:u}}}return null}function C(e){let t={};if(!e)return t;let n=e.startsWith("?")?e.slice(1):e;for(let s of n.split("&")){let[o,i]=s.split("=");if(!o)continue;let r=decodeURIComponent(o),c=i?decodeURIComponent(i):"";r in t?Array.isArray(t[r])?t[r].push(c):t[r]=[t[r],c]:t[r]=c}return t}function V(e,t){let n=[];if(!e.path)return n;let s=e.path.split("/").filter(Boolean),o="";for(let i of s){o+="/"+i;let r=t.find(c=>c.layout&&c.path===o+"/_layout");r&&n.push(r.layout)}return e.layout&&n.push(e.layout),n}var y=[],K=10;function B({routes:e,fallback:t,globalLayout:n}){return()=>{let s=h(),o=s.split("?")[0].split("#")[0],i=s.split("?")[1]?.split("#")[0]||"",r=g(),c=I(o,e);if(c){$(()=>{L.set(c.params),N.set(C(i))});let{route:u,params:f}=c,l=C(i);if(u.middleware&&u.middleware.length>0)for(let d of u.middleware){let v=d({path:o,params:f,query:l,route:u});if(v===!1)return t?a(t,{}):a("div",{class:"what-403"},a("h1",null,"403"),a("p",null,"Access denied"));if(typeof v=="string"){if(y.push(v),y.length>K){let R=y.slice(-5).join(" \u2192 ");return y.length=0,console.error(`[what-router] Redirect loop detected: ${R}`),g.set(!1),a("div",{class:"what-redirect-loop"},a("h1",null,"Redirect Loop"),a("p",null,"Too many redirects. Check your middleware configuration."))}let S=new Set,A=!1;for(let R of y){if(S.has(R)){A=!0;break}S.add(R)}if(A){let R=y.join(" \u2192 ");return y.length=0,console.error(`[what-router] Redirect cycle detected: ${R}`),g.set(!1),a("div",{class:"what-redirect-loop"},a("h1",null,"Redirect Loop"),a("p",null,"Circular redirect detected. Check your middleware configuration."))}return x(v,{replace:!0}),null}}y.length=0;let p;u.loading&&r?p=a(u.loading,{}):p=a(u.component,{params:f,query:l,route:u}),u.error&&(p=a(W,{fallback:u.error},p));let q=V(u,e);for(let d of q.reverse())p=a(d,{params:f,query:l},p);return n&&(p=a(n,{},p)),p}return t?a(t,{}):a("div",{class:"what-404"},a("h1",null,"404"),a("p",null,"Page not found"))}}function G({href:e,class:t,className:n,children:s,replace:o,prefetch:i=!0,activeClass:r="active",exactActiveClass:c="exact-active",transition:u=!0,...f}){let l=k(e)?e:"about:blank";!k(e)&&typeof console<"u"&&console.warn(`[what-router] Link blocked unsafe href: ${e}`);let p=l.split("?")[0].split("#")[0];return a("a",{href:l,class:()=>{let d=m.path,v=p==="/"?d==="/":d===p||d.startsWith(p+"/");return[t||n,v&&r,d===p&&c].filter(Boolean).join(" ")||void 0},onclick:d=>{d.ctrlKey||d.metaKey||d.shiftKey||d.altKey||d.button!==0||(d.preventDefault(),x(l,{replace:o,transition:u}))},onmouseenter:i?()=>T(l):void 0,...f},...Array.isArray(s)?s:[s])}function F(e){return G(e)}function O(e){return Object.entries(e).map(([t,n])=>typeof n=="function"?{path:t,component:n}:{path:t,...n})}function X(e,t,n={}){let{layout:s,loading:o,error:i}=n;return t.map(r=>({...r,path:e+r.path,layout:r.layout||s,loading:r.loading||o,error:r.error||i}))}function Y(e,t,n={}){let{layout:s,middleware:o}=n;return t.map(i=>({...i,_group:e,layout:i.layout||s,middleware:[...i.middleware||[],...o||[]]}))}function H({to:e}){return x(e,{replace:!0}),null}function M(e,t){return n=>function(o){let i=e(o);return i instanceof Promise?a("div",{class:"what-guard-loading"},"Loading..."):i?a(n,o):typeof t=="string"?(x(t,{replace:!0}),null):a(t,o)}}function Q(e,t={}){let{fallback:n="/login",loading:s=null}=t;return o=>function(r){let c=w("pending"),u=w(null),f=!1;return _(()=>(f=!1,Promise.resolve(e(r)).then(l=>{f||(u.set(l),c.set(l?"allowed":"denied"))}).catch(()=>{f||c.set("denied")}),()=>{f=!0})),()=>{let l=c();return l==="pending"?s?a(s,{}):null:l==="allowed"?a(o,r):typeof n=="string"?(x(n,{replace:!0}),null):a(n,r)}}}var E=new Set;function T(e){if(typeof document>"u"||E.has(e))return;E.add(e);let t=document.createElement("link");t.rel="prefetch",t.href=e,document.head.appendChild(t)}var P=new Map;function J(){typeof window>"u"||(window.addEventListener("beforeunload",()=>{P.set(location.pathname,window.scrollY)}),_(()=>{let e=m.path,t=P.get(e);requestAnimationFrame(()=>{t!==void 0?window.scrollTo(0,t):m.hash?document.querySelector(m.hash)?.scrollIntoView():window.scrollTo(0,0)})}))}function Z(e){return{style:{viewTransitionName:e}}}function ee(e){typeof document>"u"||(document.documentElement.dataset.transition=e)}function te(){return{path:b(()=>m.path),params:b(()=>m.params),query:b(()=>m.query),hash:b(()=>m.hash),isNavigating:b(()=>m.isNavigating),navigate:x,prefetch:T}}function ne({children:e}){return e||null}function re({routes:e,layout:t,fallback:n,error:s}){let o=e.map(i=>({path:i.path,component:i.component,layout:i.layout||void 0,_mode:i.mode||"client"}));return B({routes:o,globalLayout:t,fallback:n||z})}function z(){return a("div",{style:"text-align:center;padding:60px 20px"},a("h1",{style:"font-size:48px;margin-bottom:8px"},"404"),a("p",{style:"color:#64748b"},"Page not found"))}export{re as FileRouter,G as Link,F as NavLink,ne as Outlet,H as Redirect,B as Router,Q as asyncGuard,O as defineRoutes,J as enableScrollRestoration,M as guard,k as isSafeUrl,x as navigate,X as nestedRoutes,T as prefetch,m as route,Y as routeGroup,ee as setViewTransition,te as useRoute,Z as viewTransitionName};
1
+ import{signal as w,effect as _,computed as S,batch as $,h as s,ErrorBoundary as W}from"what-core";function P(t){if(typeof t!="string")return!1;let n=t.trim().replace(/[\s\x00-\x1f]/g,"").toLowerCase();return!(n.startsWith("javascript:")||n.startsWith("data:")||n.startsWith("vbscript:"))}function E(t){try{return decodeURIComponent(t)}catch{return t}}function j(t){let e={href:t,navigateTo:t,shouldIntercept:!1,isSafe:!1};if(!P(t))return e;if(typeof window>"u")return{href:t,navigateTo:t,shouldIntercept:!0,isSafe:!0};try{let n=new URL(t,window.location.href);return!(n.protocol==="http:"||n.protocol==="https:")||n.origin!==window.location.origin?{href:t,navigateTo:t,shouldIntercept:!1,isSafe:!0}:{href:t,navigateTo:n.pathname+n.search+n.hash,shouldIntercept:!0,isSafe:!0}}catch{return{href:t,navigateTo:t,shouldIntercept:!1,isSafe:!0}}}var h=w(typeof location<"u"?location.pathname+location.search+location.hash:"/"),q=w({}),A=w({}),g=w(!1),L=w(null),m={get url(){return h()},get path(){return h().split("?")[0].split("#")[0]},get params(){return q()},get query(){return A()},get hash(){let t=h().split("#")[1];return t?"#"+t:""},get isNavigating(){return g()},get error(){return L()}};async function v(t,e={}){let{replace:n=!1,state:a=null,transition:o=!0,_fromPopstate:i=!1}=e;if(!P(t)){typeof console<"u"&&console.warn(`[what-router] Blocked navigation to unsafe URL: ${t}`);return}if(typeof window<"u"&&t.startsWith("#")){let d=h().split("#")[0]+t;history.replaceState(a,"",d),h.set(d);let u=null;try{u=document.getElementById(decodeURIComponent(t.slice(1)))}catch{u=document.getElementById(t.slice(1))}u&&u.scrollIntoView({behavior:"smooth"});return}if(t===h()||g.peek())return;g.set(!0),L.set(null);let r=()=>{i||(typeof window<"u"&&b.set(h(),{x:scrollX,y:scrollY}),n?history.replaceState(a,"",t):history.pushState(a,"",t)),h.set(t)};try{if(o&&typeof document<"u"&&document.startViewTransition)try{await document.startViewTransition(r).finished}catch{h.peek()!==t&&r()}else r()}catch(c){throw L.set(c),c}finally{g.set(!1)}}typeof window<"u"&&window.addEventListener("popstate",()=>{b.set(h(),{x:scrollX,y:scrollY});let t=location.pathname+location.search+location.hash;v(t,{replace:!0,_fromPopstate:!0,transition:!1}).then(()=>{let e=b.get(t);e&&requestAnimationFrame(()=>window.scrollTo(e.x,e.y))})});function B(t){let e=t.replace(/\([\w-]+\)\//g,"").replace(/\[\.\.\.(\w+)\]/g,(r,c)=>`*:${c}`).replace(/\[(\w+)\]/g,":$1"),n=[],a=null,o=e.split("/").map(r=>r.startsWith("*:")?(a=r.slice(2),n.push(a),"(.+)"):r==="*"?(a="rest",n.push("rest"),"(.+)"):r.startsWith(":")?(n.push(r.slice(1)),"([^/]+)"):r.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")).join("/");return{regex:new RegExp(`^${o}$`),paramNames:n,catchAll:a}}function V(t,e){let a=e.filter(o=>o.path).sort((o,i)=>{let r=(o.path.match(/:/g)||[]).length+(o.path.includes("*")?100:0),c=(i.path.match(/:/g)||[]).length+(i.path.includes("*")?100:0);return r-c});for(let o of a){let{regex:i,paramNames:r}=B(o.path),c=t.match(i);if(c){let l={};return r.forEach((d,u)=>{l[d]=E(c[u+1])}),{route:o,params:l}}}return null}function C(t){let e={};if(!t)return e;let n=t.startsWith("?")?t.slice(1):t;for(let a of n.split("&")){let[o,i]=a.split("=");if(!o)continue;let r=E(o),c=i?E(i):"";r in e?Array.isArray(e[r])?e[r].push(c):e[r]=[e[r],c]:e[r]=c}return e}function K(t,e){let n=[];if(!t.path)return n;let a=t.path.split("/").filter(Boolean),o="";for(let i of a){o+="/"+i;let r=e.find(c=>c.layout&&c.path===o+"/_layout");r&&n.push(r.layout)}return t.layout&&n.push(t.layout),n}var y=[],D=10;function G({routes:t,fallback:e,globalLayout:n}){return()=>{let a=h(),o=a.split("?")[0].split("#")[0],i=a.split("?")[1]?.split("#")[0]||"",r=g(),c=V(o,t);if(c){$(()=>{q.set(c.params),A.set(C(i))});let{route:l,params:d}=c,u=C(i);if(l.middleware&&l.middleware.length>0)for(let T of l.middleware){let f=T({path:o,params:d,query:u,route:l});if(f===!1)return e?s(e,{}):s("div",{class:"what-403"},s("h1",null,"403"),s("p",null,"Access denied"));if(typeof f=="string"){if(y.push(f),y.length>D){let R=y.slice(-5).join(" \u2192 ");return y.length=0,console.error(`[what-router] Redirect loop detected: ${R}`),g.set(!1),s("div",{class:"what-redirect-loop"},s("h1",null,"Redirect Loop"),s("p",null,"Too many redirects. Check your middleware configuration."))}let k=new Set,I=!1;for(let R of y){if(k.has(R)){I=!0;break}k.add(R)}if(I){let R=y.join(" \u2192 ");return y.length=0,console.error(`[what-router] Redirect cycle detected: ${R}`),g.set(!1),s("div",{class:"what-redirect-loop"},s("h1",null,"Redirect Loop"),s("p",null,"Circular redirect detected. Check your middleware configuration."))}return v(f,{replace:!0}),null}}y.length=0;let p;l.loading&&r?p=s(l.loading,{}):p=s(l.component,{params:d,query:u,route:l}),l.error&&(p=s(W,{fallback:l.error},p));let x=K(l,t);for(let T of x.reverse())p=s(T,{params:d,query:u},p);return n&&(p=s(n,{},p)),p}return e?s(e,{}):s("div",{class:"what-404"},s("h1",null,"404"),s("p",null,"Page not found"))}}function H({href:t,class:e,className:n,children:a,replace:o,prefetch:i=!0,activeClass:r="active",exactActiveClass:c="exact-active",transition:l=!0,...d}){let u=j(t),p=u.isSafe?t:"about:blank";!u.isSafe&&typeof console<"u"&&console.warn(`[what-router] Link blocked unsafe href: ${t}`);let x=u.shouldIntercept?u.navigateTo.split("?")[0].split("#")[0]:"";return s("a",{href:p,class:()=>{let f=m.path,k=u.shouldIntercept&&(x==="/"?f==="/":f===x||f.startsWith(x+"/")),I=u.shouldIntercept&&f===x;return[e||n,k&&r,I&&c].filter(Boolean).join(" ")||void 0},onclick:f=>{f.ctrlKey||f.metaKey||f.shiftKey||f.altKey||f.button!==0||u.shouldIntercept&&(f.preventDefault(),v(u.navigateTo,{replace:o,transition:l}))},onmouseenter:i?()=>N(p):void 0,...d},...Array.isArray(a)?a:[a])}function O(t){return H(t)}function X(t){return Object.entries(t).map(([e,n])=>typeof n=="function"?{path:e,component:n}:{path:e,...n})}function Y(t,e,n={}){let{layout:a,loading:o,error:i}=n;return e.map(r=>({...r,path:t+r.path,layout:r.layout||a,loading:r.loading||o,error:r.error||i}))}function M(t,e,n={}){let{layout:a,middleware:o}=n;return e.map(i=>({...i,_group:t,layout:i.layout||a,middleware:[...i.middleware||[],...o||[]]}))}function Q({to:t}){return v(t,{replace:!0}),null}function J(t,e){return n=>function(o){let i=t(o);return i instanceof Promise?s("div",{class:"what-guard-loading"},"Loading..."):i?s(n,o):typeof e=="string"?(v(e,{replace:!0}),null):s(e,o)}}function Z(t,e={}){let{fallback:n="/login",loading:a=null}=e;return o=>function(r){let c=w("pending"),l=w(null),d=!1;return _(()=>(d=!1,Promise.resolve(t(r)).then(u=>{d||(l.set(u),c.set(u?"allowed":"denied"))}).catch(()=>{d||c.set("denied")}),()=>{d=!0})),()=>{let u=c();return u==="pending"?a?s(a,{}):null:u==="allowed"?s(o,r):typeof n=="string"?(v(n,{replace:!0}),null):s(n,r)}}}var U=new Set;function N(t){if(typeof document>"u")return;if(!P(t)){typeof console<"u"&&console.warn(`[what-router] Blocked prefetch for unsafe URL: ${t}`);return}if(U.has(t))return;U.add(t);let e=document.createElement("link");e.rel="prefetch",e.href=t,document.head.appendChild(e)}var b=new Map;function tt(){typeof window>"u"||(window.addEventListener("beforeunload",()=>{b.set(location.pathname,window.scrollY)}),_(()=>{let t=m.path,e=b.get(t);requestAnimationFrame(()=>{e!==void 0?window.scrollTo(0,e):m.hash?document.querySelector(m.hash)?.scrollIntoView():window.scrollTo(0,0)})}))}function et(t){return{style:{viewTransitionName:t}}}function nt(t){typeof document>"u"||(document.documentElement.dataset.transition=t)}function rt(){return{path:S(()=>m.path),params:S(()=>m.params),query:S(()=>m.query),hash:S(()=>m.hash),isNavigating:S(()=>m.isNavigating),navigate:v,prefetch:N}}function ot({children:t}){return t||null}function it({routes:t,layout:e,fallback:n,error:a}){let o=t.map(i=>({path:i.path,component:i.component,layout:i.layout||void 0,_mode:i.mode||"client"}));return G({routes:o,globalLayout:e,fallback:n||z})}function z(){return s("div",{style:"text-align:center;padding:60px 20px"},s("h1",{style:"font-size:48px;margin-bottom:8px"},"404"),s("p",{style:"color:#64748b"},"Page not found"))}export{it as FileRouter,H as Link,O as NavLink,ot as Outlet,Q as Redirect,G as Router,Z as asyncGuard,X as defineRoutes,tt as enableScrollRestoration,J as guard,P as isSafeUrl,v as navigate,Y as nestedRoutes,N as prefetch,m as route,M as routeGroup,nt as setViewTransition,rt as useRoute,et as viewTransitionName};
2
2
  //# sourceMappingURL=index.min.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/index.js"],
4
- "sourcesContent": ["// What Framework - Router\n// Production-grade file-based routing with nested layouts, loading states,\n// route groups, view transitions, and middleware.\n\nimport { signal, effect, computed, batch, h, ErrorBoundary } from 'what-core';\n\n// --- URL Sanitization ---\n// Rejects javascript:, data:, vbscript: protocols (case-insensitive, trimmed).\n\nexport function isSafeUrl(url) {\n if (typeof url !== 'string') return false;\n const trimmed = url.trim();\n // Check for dangerous protocols (case-insensitive, ignoring whitespace/control chars)\n const normalized = trimmed.replace(/[\\s\\x00-\\x1f]/g, '').toLowerCase();\n if (normalized.startsWith('javascript:')) return false;\n if (normalized.startsWith('data:')) return false;\n if (normalized.startsWith('vbscript:')) return false;\n return true;\n}\n\n// --- Route State (global singleton) ---\n\nconst _url = signal(typeof location !== 'undefined' ? location.pathname + location.search + location.hash : '/');\nconst _params = signal({});\nconst _query = signal({});\nconst _isNavigating = signal(false);\nconst _navigationError = signal(null);\n\nexport const route = {\n get url() { return _url(); },\n get path() { return _url().split('?')[0].split('#')[0]; },\n get params() { return _params(); },\n get query() { return _query(); },\n get hash() {\n const h = _url().split('#')[1];\n return h ? '#' + h : '';\n },\n get isNavigating() { return _isNavigating(); },\n get error() { return _navigationError(); },\n};\n\n// --- Navigation with View Transitions ---\n\nexport async function navigate(to, opts = {}) {\n const { replace = false, state = null, transition = true, _fromPopstate = false } = opts;\n\n // Reject unsafe URLs\n if (!isSafeUrl(to)) {\n if (typeof console !== 'undefined') {\n console.warn(`[what-router] Blocked navigation to unsafe URL: ${to}`);\n }\n return;\n }\n\n // Handle same-page hash links \u2014 use replaceState and scroll directly\n if (typeof window !== 'undefined' && to.startsWith('#')) {\n const currentUrl = _url();\n const basePath = currentUrl.split('#')[0];\n const newUrl = basePath + to;\n history.replaceState(state, '', newUrl);\n _url.set(newUrl);\n const el = document.querySelector(to);\n if (el) el.scrollIntoView({ behavior: 'smooth' });\n return;\n }\n\n // Don't navigate if already on the same URL\n if (to === _url()) return;\n\n // Prevent concurrent navigations \u2014 wait for current to finish\n if (_isNavigating.peek()) return;\n\n _isNavigating.set(true);\n _navigationError.set(null);\n\n const doNavigation = () => {\n // Skip history manipulation on popstate (browser already updated the URL)\n if (!_fromPopstate) {\n // Save scroll position for current URL before navigating away\n if (typeof window !== 'undefined') {\n scrollPositions.set(_url(), { x: scrollX, y: scrollY });\n }\n if (replace) {\n history.replaceState(state, '', to);\n } else {\n history.pushState(state, '', to);\n }\n }\n _url.set(to);\n _isNavigating.set(false);\n };\n\n // Use View Transitions API if available and enabled\n if (transition && typeof document !== 'undefined' && document.startViewTransition) {\n try {\n await document.startViewTransition(doNavigation).finished;\n } catch (e) {\n // Transition failed, navigation still happened\n }\n } else {\n doNavigation();\n }\n}\n\n// Back/forward support \u2014 route through navigate() so middleware runs\nif (typeof window !== 'undefined') {\n window.addEventListener('popstate', () => {\n // Save scroll position for the URL we're leaving\n scrollPositions.set(_url(), { x: scrollX, y: scrollY });\n\n const newUrl = location.pathname + location.search + location.hash;\n // Use _fromPopstate flag so navigate() skips pushState (browser already updated URL)\n navigate(newUrl, { replace: true, _fromPopstate: true, transition: false }).then(() => {\n // Restore saved scroll position for the URL we're arriving at\n const saved = scrollPositions.get(newUrl);\n if (saved) {\n requestAnimationFrame(() => window.scrollTo(saved.x, saved.y));\n }\n });\n });\n}\n\n// --- Route Matching ---\n\nfunction compilePath(path) {\n // /users/:id -> regex + param names\n // /posts/* -> catch-all\n // /[slug] -> dynamic (file-based syntax)\n // (group) -> route group (ignored in URL)\n\n // Remove route groups from path (they don't affect URL matching)\n const normalized = path\n .replace(/\\([\\w-]+\\)\\//g, '') // Remove (group)/ prefixes\n .replace(/\\[\\.\\.\\.(\\w+)\\]/g, (_, name) => `*:${name}`) // Preserve catch-all name\n .replace(/\\[(\\w+)\\]/g, ':$1'); // File-based [param] to :param\n\n const paramNames = [];\n let catchAll = null;\n\n const regexStr = normalized\n .split('/')\n .map(segment => {\n if (segment.startsWith('*:')) {\n catchAll = segment.slice(2);\n paramNames.push(catchAll);\n return '(.+)';\n }\n if (segment === '*') {\n catchAll = 'rest';\n paramNames.push('rest');\n return '(.+)';\n }\n if (segment.startsWith(':')) {\n paramNames.push(segment.slice(1));\n return '([^/]+)';\n }\n return segment.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n })\n .join('/');\n\n const regex = new RegExp(`^${regexStr}$`);\n return { regex, paramNames, catchAll };\n}\n\nfunction matchRoute(path, routes) {\n // Filter out routes without a path (layout-only routes, etc.)\n const routable = routes.filter(r => r.path);\n\n // Sort routes by specificity (more specific first)\n const sorted = routable.sort((a, b) => {\n const aSpecific = (a.path.match(/:/g) || []).length + (a.path.includes('*') ? 100 : 0);\n const bSpecific = (b.path.match(/:/g) || []).length + (b.path.includes('*') ? 100 : 0);\n return aSpecific - bSpecific;\n });\n\n for (const route of sorted) {\n const { regex, paramNames } = compilePath(route.path);\n const match = path.match(regex);\n if (match) {\n const params = {};\n paramNames.forEach((name, i) => {\n params[name] = decodeURIComponent(match[i + 1]);\n });\n return { route, params };\n }\n }\n return null;\n}\n\nfunction parseQuery(search) {\n const params = {};\n if (!search) return params;\n const qs = search.startsWith('?') ? search.slice(1) : search;\n for (const pair of qs.split('&')) {\n const [key, val] = pair.split('=');\n if (!key) continue;\n const decodedKey = decodeURIComponent(key);\n const decodedVal = val ? decodeURIComponent(val) : '';\n if (decodedKey in params) {\n // Collect repeated keys into arrays\n if (Array.isArray(params[decodedKey])) {\n params[decodedKey].push(decodedVal);\n } else {\n params[decodedKey] = [params[decodedKey], decodedVal];\n }\n } else {\n params[decodedKey] = decodedVal;\n }\n }\n return params;\n}\n\n// --- Nested Layouts ---\n\n// Build the layout chain for a route\nfunction buildLayoutChain(route, routes) {\n const layouts = [];\n if (!route.path) return layouts;\n\n // Check for nested layouts based on path segments\n const segments = route.path.split('/').filter(Boolean);\n let currentPath = '';\n\n for (const segment of segments) {\n currentPath += '/' + segment;\n\n // Find layout for this path level\n const layoutRoute = routes.find(r =>\n r.layout && r.path === currentPath + '/_layout'\n );\n if (layoutRoute) {\n layouts.push(layoutRoute.layout);\n }\n }\n\n // Add route's own layout if specified\n if (route.layout) {\n layouts.push(route.layout);\n }\n\n return layouts;\n}\n\n// --- Middleware redirect loop detection ---\nconst _redirectHistory = [];\nconst MAX_REDIRECTS = 10;\n\n// --- Router Component ---\n\nexport function Router({ routes, fallback, globalLayout }) {\n // Return a reactive function child. The Router component runs ONCE,\n // but the returned function re-evaluates whenever _url changes,\n // and the fine-grained runtime updates the DOM accordingly.\n return () => {\n const currentUrl = _url();\n const path = currentUrl.split('?')[0].split('#')[0];\n const search = currentUrl.split('?')[1]?.split('#')[0] || '';\n const isNavigating = _isNavigating();\n\n const matched = matchRoute(path, routes);\n\n if (matched) {\n batch(() => {\n _params.set(matched.params);\n _query.set(parseQuery(search));\n });\n\n const { route: r, params } = matched;\n const queryObj = parseQuery(search);\n\n // Run middleware (sync only \u2014 async middleware should use asyncGuard)\n if (r.middleware && r.middleware.length > 0) {\n for (const mw of r.middleware) {\n const result = mw({ path, params, query: queryObj, route: r });\n if (result === false) {\n // Middleware rejected \u2014 show fallback\n if (fallback) return h(fallback, {});\n return h('div', { class: 'what-403' }, h('h1', null, '403'), h('p', null, 'Access denied'));\n }\n if (typeof result === 'string') {\n // Redirect loop detection\n _redirectHistory.push(result);\n if (_redirectHistory.length > MAX_REDIRECTS) {\n const cycle = _redirectHistory.slice(-5).join(' \u2192 ');\n _redirectHistory.length = 0;\n console.error(`[what-router] Redirect loop detected: ${cycle}`);\n _isNavigating.set(false);\n return h('div', { class: 'what-redirect-loop' },\n h('h1', null, 'Redirect Loop'),\n h('p', null, 'Too many redirects. Check your middleware configuration.')\n );\n }\n // Check for direct cycle (A \u2192 B \u2192 A)\n const seen = new Set();\n let hasCycle = false;\n for (const url of _redirectHistory) {\n if (seen.has(url)) { hasCycle = true; break; }\n seen.add(url);\n }\n if (hasCycle) {\n const cycle = _redirectHistory.join(' \u2192 ');\n _redirectHistory.length = 0;\n console.error(`[what-router] Redirect cycle detected: ${cycle}`);\n _isNavigating.set(false);\n return h('div', { class: 'what-redirect-loop' },\n h('h1', null, 'Redirect Loop'),\n h('p', null, 'Circular redirect detected. Check your middleware configuration.')\n );\n }\n // Middleware returned a redirect path\n navigate(result, { replace: true });\n return null;\n }\n }\n }\n // Successful render \u2014 clear redirect history\n _redirectHistory.length = 0;\n\n // Build element with loading state support\n let element;\n\n if (r.loading && isNavigating) {\n element = h(r.loading, {});\n } else {\n element = h(r.component, {\n params,\n query: queryObj,\n route: r,\n });\n }\n\n // Wrap with per-route error boundary if specified\n if (r.error) {\n element = h(ErrorBoundary, { fallback: r.error }, element);\n }\n\n // Wrap with nested layouts (innermost to outermost)\n const layouts = buildLayoutChain(r, routes);\n for (const Layout of layouts.reverse()) {\n element = h(Layout, { params, query: queryObj }, element);\n }\n\n // Global layout wrapper\n if (globalLayout) {\n element = h(globalLayout, {}, element);\n }\n\n return element;\n }\n\n // 404\n if (fallback) return h(fallback, {});\n return h('div', { class: 'what-404' },\n h('h1', null, '404'),\n h('p', null, 'Page not found')\n );\n };\n}\n\n// --- Link Component ---\n\nexport function Link({\n href,\n class: cls,\n className,\n children,\n replace: rep,\n prefetch: shouldPrefetch = true,\n activeClass = 'active',\n exactActiveClass = 'exact-active',\n transition = true,\n ...rest\n}) {\n // Sanitize href \u2014 reject dangerous protocols\n const safeHref = isSafeUrl(href) ? href : 'about:blank';\n if (!isSafeUrl(href) && typeof console !== 'undefined') {\n console.warn(`[what-router] Link blocked unsafe href: ${href}`);\n }\n\n // Strip query string and hash from href for path comparison\n const hrefPath = safeHref.split('?')[0].split('#')[0];\n\n // Use a reactive function for class so active states update on navigation.\n // In the run-once model, reading route.path directly would snapshot it.\n const reactiveClass = () => {\n const currentPath = route.path;\n const isActive = hrefPath === '/'\n ? currentPath === '/'\n : currentPath === hrefPath || currentPath.startsWith(hrefPath + '/');\n const isExactActive = currentPath === hrefPath;\n\n return [\n cls || className,\n isActive && activeClass,\n isExactActive && exactActiveClass,\n ].filter(Boolean).join(' ') || undefined;\n };\n\n return h('a', {\n href: safeHref,\n class: reactiveClass,\n onclick: (e) => {\n // Only intercept left-clicks without modifiers\n if (e.ctrlKey || e.metaKey || e.shiftKey || e.altKey || e.button !== 0) return;\n e.preventDefault();\n navigate(safeHref, { replace: rep, transition });\n },\n onmouseenter: shouldPrefetch ? () => prefetch(safeHref) : undefined,\n ...rest,\n }, ...(Array.isArray(children) ? children : [children]));\n}\n\n// --- NavLink with active states ---\n\nexport function NavLink(props) {\n return Link(props);\n}\n\n// --- Define Routes Helper ---\n// Creates route config from a flat object for convenience.\n\nexport function defineRoutes(config) {\n return Object.entries(config).map(([path, value]) => {\n if (typeof value === 'function') {\n return { path, component: value };\n }\n // Object form with layout, middleware, loading, error, etc.\n return { path, ...value };\n });\n}\n\n// --- Nested Route Helper ---\n\nexport function nestedRoutes(basePath, children, options = {}) {\n const { layout, loading, error } = options;\n\n return children.map(child => ({\n ...child,\n path: basePath + child.path,\n layout: child.layout || layout,\n loading: child.loading || loading,\n error: child.error || error,\n }));\n}\n\n// --- Route Groups ---\n// Group routes without affecting URL structure\n\nexport function routeGroup(name, routes, options = {}) {\n const { layout, middleware } = options;\n\n return routes.map(route => ({\n ...route,\n _group: name,\n layout: route.layout || layout,\n middleware: [...(route.middleware || []), ...(middleware || [])],\n }));\n}\n\n// --- Redirect ---\n\nexport function Redirect({ to }) {\n navigate(to, { replace: true });\n return null;\n}\n\n// --- Route Guards / Middleware ---\n\nexport function guard(check, fallback) {\n return (Component) => {\n return function GuardedRoute(props) {\n const result = check(props);\n\n // Support async guards\n if (result instanceof Promise) {\n // Return loading while checking\n return h('div', { class: 'what-guard-loading' }, 'Loading...');\n }\n\n if (result) {\n return h(Component, props);\n }\n\n if (typeof fallback === 'string') {\n navigate(fallback, { replace: true });\n return null;\n }\n return h(fallback, props);\n };\n };\n}\n\n// Async guard with suspense\nexport function asyncGuard(check, options = {}) {\n const { fallback = '/login', loading = null } = options;\n\n return (Component) => {\n return function AsyncGuardedRoute(props) {\n const status = signal('pending');\n const checkResult = signal(null);\n let cancelled = false;\n\n effect(() => {\n cancelled = false;\n Promise.resolve(check(props))\n .then(result => {\n if (cancelled) return;\n checkResult.set(result);\n status.set(result ? 'allowed' : 'denied');\n })\n .catch(() => {\n if (!cancelled) status.set('denied');\n });\n return () => { cancelled = true; };\n });\n\n // Return a reactive function child so status changes update the DOM.\n // Components run once, so reading status() outside a reactive wrapper\n // would snapshot the value and never update.\n return () => {\n const currentStatus = status();\n\n if (currentStatus === 'pending') {\n return loading ? h(loading, {}) : null;\n }\n\n if (currentStatus === 'allowed') {\n return h(Component, props);\n }\n\n if (typeof fallback === 'string') {\n navigate(fallback, { replace: true });\n return null;\n }\n return h(fallback, props);\n };\n };\n };\n}\n\n// --- Prefetch ---\n// Hint the browser to prefetch a route's assets.\n\nconst prefetchedUrls = new Set();\n\nexport function prefetch(href) {\n if (typeof document === 'undefined') return;\n if (prefetchedUrls.has(href)) return;\n prefetchedUrls.add(href);\n\n const link = document.createElement('link');\n link.rel = 'prefetch';\n link.href = href;\n document.head.appendChild(link);\n}\n\n// --- Scroll Restoration ---\n\nconst scrollPositions = new Map();\n\nexport function enableScrollRestoration() {\n if (typeof window === 'undefined') return;\n\n // Save scroll position before navigation\n window.addEventListener('beforeunload', () => {\n scrollPositions.set(location.pathname, window.scrollY);\n });\n\n // Restore scroll position after navigation\n effect(() => {\n const path = route.path;\n const savedPosition = scrollPositions.get(path);\n\n requestAnimationFrame(() => {\n if (savedPosition !== undefined) {\n window.scrollTo(0, savedPosition);\n } else if (route.hash) {\n const el = document.querySelector(route.hash);\n el?.scrollIntoView();\n } else {\n window.scrollTo(0, 0);\n }\n });\n });\n}\n\n// --- View Transition Helpers ---\n\nexport function viewTransitionName(name) {\n return { style: { viewTransitionName: name } };\n}\n\n// Configure view transition types\nexport function setViewTransition(type) {\n if (typeof document === 'undefined') return;\n document.documentElement.dataset.transition = type;\n}\n\n// --- useRoute Hook ---\n\nexport function useRoute() {\n return {\n path: computed(() => route.path),\n params: computed(() => route.params),\n query: computed(() => route.query),\n hash: computed(() => route.hash),\n isNavigating: computed(() => route.isNavigating),\n navigate,\n prefetch,\n };\n}\n\n// --- Outlet Component ---\n// For nested route rendering\n\nexport function Outlet({ children }) {\n // Children passed from parent layout\n return children || null;\n}\n\n// --- File-Based Router ---\n// Consumes routes generated by what-compiler's file router (virtual:what-routes).\n// Usage:\n// import { routes } from 'virtual:what-routes';\n// mount(<FileRouter routes={routes} />, '#app');\n\nexport function FileRouter({\n routes,\n layout: globalLayout,\n fallback,\n error: globalError,\n}) {\n // Convert file-router route format to Router's expected format\n const routerRoutes = routes.map(r => ({\n path: r.path,\n component: r.component,\n layout: r.layout || undefined,\n // Attach page mode as metadata for build system\n _mode: r.mode || 'client',\n }));\n\n // Router already returns a reactive function child \u2014 just delegate\n return Router({\n routes: routerRoutes,\n globalLayout,\n fallback: fallback || Default404,\n });\n}\n\nfunction Default404() {\n return h('div', { style: 'text-align:center;padding:60px 20px' },\n h('h1', { style: 'font-size:48px;margin-bottom:8px' }, '404'),\n h('p', { style: 'color:#64748b' }, 'Page not found'),\n );\n}\n"],
5
- "mappings": "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",
6
- "names": ["signal", "effect", "computed", "batch", "h", "ErrorBoundary", "isSafeUrl", "url", "normalized", "_url", "_params", "_query", "_isNavigating", "_navigationError", "route", "navigate", "to", "opts", "replace", "state", "transition", "_fromPopstate", "newUrl", "el", "doNavigation", "scrollPositions", "saved", "compilePath", "path", "_", "name", "paramNames", "catchAll", "regexStr", "segment", "matchRoute", "routes", "sorted", "r", "a", "b", "aSpecific", "bSpecific", "regex", "match", "params", "i", "parseQuery", "search", "qs", "pair", "key", "val", "decodedKey", "decodedVal", "buildLayoutChain", "layouts", "segments", "currentPath", "layoutRoute", "_redirectHistory", "MAX_REDIRECTS", "Router", "fallback", "globalLayout", "currentUrl", "isNavigating", "matched", "queryObj", "mw", "result", "cycle", "seen", "hasCycle", "element", "Layout", "Link", "href", "cls", "className", "children", "rep", "shouldPrefetch", "activeClass", "exactActiveClass", "rest", "safeHref", "hrefPath", "isActive", "e", "prefetch", "NavLink", "props", "defineRoutes", "config", "value", "nestedRoutes", "basePath", "options", "layout", "loading", "error", "child", "routeGroup", "middleware", "Redirect", "guard", "check", "Component", "asyncGuard", "status", "checkResult", "cancelled", "currentStatus", "prefetchedUrls", "link", "enableScrollRestoration", "savedPosition", "viewTransitionName", "setViewTransition", "type", "useRoute", "Outlet", "FileRouter", "globalError", "routerRoutes", "Default404"]
4
+ "sourcesContent": ["// What Framework - Router\n// Production-grade file-based routing with nested layouts, loading states,\n// route groups, view transitions, and middleware.\n\nimport { signal, effect, computed, batch, h, ErrorBoundary } from 'what-core';\n\n// --- URL Sanitization ---\n// Rejects javascript:, data:, vbscript: protocols (case-insensitive, trimmed).\n\nexport function isSafeUrl(url) {\n if (typeof url !== 'string') return false;\n const trimmed = url.trim();\n // Check for dangerous protocols (case-insensitive, ignoring whitespace/control chars)\n const normalized = trimmed.replace(/[\\s\\x00-\\x1f]/g, '').toLowerCase();\n if (normalized.startsWith('javascript:')) return false;\n if (normalized.startsWith('data:')) return false;\n if (normalized.startsWith('vbscript:')) return false;\n return true;\n}\n\nfunction safeDecodeURIComponent(value) {\n try {\n return decodeURIComponent(value);\n } catch {\n return value;\n }\n}\n\nfunction getLinkTarget(href) {\n const fallback = { href, navigateTo: href, shouldIntercept: false, isSafe: false };\n if (!isSafeUrl(href)) return fallback;\n\n if (typeof window === 'undefined') {\n return { href, navigateTo: href, shouldIntercept: true, isSafe: true };\n }\n\n try {\n const url = new URL(href, window.location.href);\n const isHttp = url.protocol === 'http:' || url.protocol === 'https:';\n if (!isHttp || url.origin !== window.location.origin) {\n return { href, navigateTo: href, shouldIntercept: false, isSafe: true };\n }\n return {\n href,\n navigateTo: url.pathname + url.search + url.hash,\n shouldIntercept: true,\n isSafe: true,\n };\n } catch {\n return { href, navigateTo: href, shouldIntercept: false, isSafe: true };\n }\n}\n\n// --- Route State (global singleton) ---\n\nconst _url = signal(typeof location !== 'undefined' ? location.pathname + location.search + location.hash : '/');\nconst _params = signal({});\nconst _query = signal({});\nconst _isNavigating = signal(false);\nconst _navigationError = signal(null);\n\nexport const route = {\n get url() { return _url(); },\n get path() { return _url().split('?')[0].split('#')[0]; },\n get params() { return _params(); },\n get query() { return _query(); },\n get hash() {\n const h = _url().split('#')[1];\n return h ? '#' + h : '';\n },\n get isNavigating() { return _isNavigating(); },\n get error() { return _navigationError(); },\n};\n\n// --- Navigation with View Transitions ---\n\nexport async function navigate(to, opts = {}) {\n const { replace = false, state = null, transition = true, _fromPopstate = false } = opts;\n\n // Reject unsafe URLs\n if (!isSafeUrl(to)) {\n if (typeof console !== 'undefined') {\n console.warn(`[what-router] Blocked navigation to unsafe URL: ${to}`);\n }\n return;\n }\n\n // Handle same-page hash links \u2014 use replaceState and scroll directly\n if (typeof window !== 'undefined' && to.startsWith('#')) {\n const currentUrl = _url();\n const basePath = currentUrl.split('#')[0];\n const newUrl = basePath + to;\n history.replaceState(state, '', newUrl);\n _url.set(newUrl);\n let el = null;\n try {\n el = document.getElementById(decodeURIComponent(to.slice(1)));\n } catch {\n el = document.getElementById(to.slice(1));\n }\n if (el) el.scrollIntoView({ behavior: 'smooth' });\n return;\n }\n\n // Don't navigate if already on the same URL\n if (to === _url()) return;\n\n // Prevent concurrent navigations \u2014 wait for current to finish\n if (_isNavigating.peek()) return;\n\n _isNavigating.set(true);\n _navigationError.set(null);\n\n const doNavigation = () => {\n // Skip history manipulation on popstate (browser already updated the URL)\n if (!_fromPopstate) {\n // Save scroll position for current URL before navigating away\n if (typeof window !== 'undefined') {\n scrollPositions.set(_url(), { x: scrollX, y: scrollY });\n }\n if (replace) {\n history.replaceState(state, '', to);\n } else {\n history.pushState(state, '', to);\n }\n }\n _url.set(to);\n };\n\n try {\n // Use View Transitions API if available and enabled\n if (transition && typeof document !== 'undefined' && document.startViewTransition) {\n try {\n await document.startViewTransition(doNavigation).finished;\n } catch (e) {\n // Transition failed; fall back to direct navigation if it did not run.\n if (_url.peek() !== to) doNavigation();\n }\n } else {\n doNavigation();\n }\n } catch (e) {\n _navigationError.set(e);\n throw e;\n } finally {\n _isNavigating.set(false);\n }\n}\n\n// Back/forward support \u2014 route through navigate() so middleware runs\nif (typeof window !== 'undefined') {\n window.addEventListener('popstate', () => {\n // Save scroll position for the URL we're leaving\n scrollPositions.set(_url(), { x: scrollX, y: scrollY });\n\n const newUrl = location.pathname + location.search + location.hash;\n // Use _fromPopstate flag so navigate() skips pushState (browser already updated URL)\n navigate(newUrl, { replace: true, _fromPopstate: true, transition: false }).then(() => {\n // Restore saved scroll position for the URL we're arriving at\n const saved = scrollPositions.get(newUrl);\n if (saved) {\n requestAnimationFrame(() => window.scrollTo(saved.x, saved.y));\n }\n });\n });\n}\n\n// --- Route Matching ---\n\nfunction compilePath(path) {\n // /users/:id -> regex + param names\n // /posts/* -> catch-all\n // /[slug] -> dynamic (file-based syntax)\n // (group) -> route group (ignored in URL)\n\n // Remove route groups from path (they don't affect URL matching)\n const normalized = path\n .replace(/\\([\\w-]+\\)\\//g, '') // Remove (group)/ prefixes\n .replace(/\\[\\.\\.\\.(\\w+)\\]/g, (_, name) => `*:${name}`) // Preserve catch-all name\n .replace(/\\[(\\w+)\\]/g, ':$1'); // File-based [param] to :param\n\n const paramNames = [];\n let catchAll = null;\n\n const regexStr = normalized\n .split('/')\n .map(segment => {\n if (segment.startsWith('*:')) {\n catchAll = segment.slice(2);\n paramNames.push(catchAll);\n return '(.+)';\n }\n if (segment === '*') {\n catchAll = 'rest';\n paramNames.push('rest');\n return '(.+)';\n }\n if (segment.startsWith(':')) {\n paramNames.push(segment.slice(1));\n return '([^/]+)';\n }\n return segment.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n })\n .join('/');\n\n const regex = new RegExp(`^${regexStr}$`);\n return { regex, paramNames, catchAll };\n}\n\nfunction matchRoute(path, routes) {\n // Filter out routes without a path (layout-only routes, etc.)\n const routable = routes.filter(r => r.path);\n\n // Sort routes by specificity (more specific first)\n const sorted = routable.sort((a, b) => {\n const aSpecific = (a.path.match(/:/g) || []).length + (a.path.includes('*') ? 100 : 0);\n const bSpecific = (b.path.match(/:/g) || []).length + (b.path.includes('*') ? 100 : 0);\n return aSpecific - bSpecific;\n });\n\n for (const route of sorted) {\n const { regex, paramNames } = compilePath(route.path);\n const match = path.match(regex);\n if (match) {\n const params = {};\n paramNames.forEach((name, i) => {\n params[name] = safeDecodeURIComponent(match[i + 1]);\n });\n return { route, params };\n }\n }\n return null;\n}\n\nfunction parseQuery(search) {\n const params = {};\n if (!search) return params;\n const qs = search.startsWith('?') ? search.slice(1) : search;\n for (const pair of qs.split('&')) {\n const [key, val] = pair.split('=');\n if (!key) continue;\n const decodedKey = safeDecodeURIComponent(key);\n const decodedVal = val ? safeDecodeURIComponent(val) : '';\n if (decodedKey in params) {\n // Collect repeated keys into arrays\n if (Array.isArray(params[decodedKey])) {\n params[decodedKey].push(decodedVal);\n } else {\n params[decodedKey] = [params[decodedKey], decodedVal];\n }\n } else {\n params[decodedKey] = decodedVal;\n }\n }\n return params;\n}\n\n// --- Nested Layouts ---\n\n// Build the layout chain for a route\nfunction buildLayoutChain(route, routes) {\n const layouts = [];\n if (!route.path) return layouts;\n\n // Check for nested layouts based on path segments\n const segments = route.path.split('/').filter(Boolean);\n let currentPath = '';\n\n for (const segment of segments) {\n currentPath += '/' + segment;\n\n // Find layout for this path level\n const layoutRoute = routes.find(r =>\n r.layout && r.path === currentPath + '/_layout'\n );\n if (layoutRoute) {\n layouts.push(layoutRoute.layout);\n }\n }\n\n // Add route's own layout if specified\n if (route.layout) {\n layouts.push(route.layout);\n }\n\n return layouts;\n}\n\n// --- Middleware redirect loop detection ---\nconst _redirectHistory = [];\nconst MAX_REDIRECTS = 10;\n\n// --- Router Component ---\n\nexport function Router({ routes, fallback, globalLayout }) {\n // Return a reactive function child. The Router component runs ONCE,\n // but the returned function re-evaluates whenever _url changes,\n // and the fine-grained runtime updates the DOM accordingly.\n return () => {\n const currentUrl = _url();\n const path = currentUrl.split('?')[0].split('#')[0];\n const search = currentUrl.split('?')[1]?.split('#')[0] || '';\n const isNavigating = _isNavigating();\n\n const matched = matchRoute(path, routes);\n\n if (matched) {\n batch(() => {\n _params.set(matched.params);\n _query.set(parseQuery(search));\n });\n\n const { route: r, params } = matched;\n const queryObj = parseQuery(search);\n\n // Run middleware (sync only \u2014 async middleware should use asyncGuard)\n if (r.middleware && r.middleware.length > 0) {\n for (const mw of r.middleware) {\n const result = mw({ path, params, query: queryObj, route: r });\n if (result === false) {\n // Middleware rejected \u2014 show fallback\n if (fallback) return h(fallback, {});\n return h('div', { class: 'what-403' }, h('h1', null, '403'), h('p', null, 'Access denied'));\n }\n if (typeof result === 'string') {\n // Redirect loop detection\n _redirectHistory.push(result);\n if (_redirectHistory.length > MAX_REDIRECTS) {\n const cycle = _redirectHistory.slice(-5).join(' \u2192 ');\n _redirectHistory.length = 0;\n console.error(`[what-router] Redirect loop detected: ${cycle}`);\n _isNavigating.set(false);\n return h('div', { class: 'what-redirect-loop' },\n h('h1', null, 'Redirect Loop'),\n h('p', null, 'Too many redirects. Check your middleware configuration.')\n );\n }\n // Check for direct cycle (A \u2192 B \u2192 A)\n const seen = new Set();\n let hasCycle = false;\n for (const url of _redirectHistory) {\n if (seen.has(url)) { hasCycle = true; break; }\n seen.add(url);\n }\n if (hasCycle) {\n const cycle = _redirectHistory.join(' \u2192 ');\n _redirectHistory.length = 0;\n console.error(`[what-router] Redirect cycle detected: ${cycle}`);\n _isNavigating.set(false);\n return h('div', { class: 'what-redirect-loop' },\n h('h1', null, 'Redirect Loop'),\n h('p', null, 'Circular redirect detected. Check your middleware configuration.')\n );\n }\n // Middleware returned a redirect path\n navigate(result, { replace: true });\n return null;\n }\n }\n }\n // Successful render \u2014 clear redirect history\n _redirectHistory.length = 0;\n\n // Build element with loading state support\n let element;\n\n if (r.loading && isNavigating) {\n element = h(r.loading, {});\n } else {\n element = h(r.component, {\n params,\n query: queryObj,\n route: r,\n });\n }\n\n // Wrap with per-route error boundary if specified\n if (r.error) {\n element = h(ErrorBoundary, { fallback: r.error }, element);\n }\n\n // Wrap with nested layouts (innermost to outermost)\n const layouts = buildLayoutChain(r, routes);\n for (const Layout of layouts.reverse()) {\n element = h(Layout, { params, query: queryObj }, element);\n }\n\n // Global layout wrapper\n if (globalLayout) {\n element = h(globalLayout, {}, element);\n }\n\n return element;\n }\n\n // 404\n if (fallback) return h(fallback, {});\n return h('div', { class: 'what-404' },\n h('h1', null, '404'),\n h('p', null, 'Page not found')\n );\n };\n}\n\n// --- Link Component ---\n\nexport function Link({\n href,\n class: cls,\n className,\n children,\n replace: rep,\n prefetch: shouldPrefetch = true,\n activeClass = 'active',\n exactActiveClass = 'exact-active',\n transition = true,\n ...rest\n}) {\n // Sanitize href \u2014 reject dangerous protocols and only intercept same-origin routes.\n const target = getLinkTarget(href);\n const safeHref = target.isSafe ? href : 'about:blank';\n if (!target.isSafe && typeof console !== 'undefined') {\n console.warn(`[what-router] Link blocked unsafe href: ${href}`);\n }\n\n // Strip query string and hash from same-origin targets for path comparison.\n const hrefPath = target.shouldIntercept\n ? target.navigateTo.split('?')[0].split('#')[0]\n : '';\n\n // Use a reactive function for class so active states update on navigation.\n // In the run-once model, reading route.path directly would snapshot it.\n const reactiveClass = () => {\n const currentPath = route.path;\n const isActive = target.shouldIntercept && (hrefPath === '/'\n ? currentPath === '/'\n : currentPath === hrefPath || currentPath.startsWith(hrefPath + '/'));\n const isExactActive = target.shouldIntercept && currentPath === hrefPath;\n\n return [\n cls || className,\n isActive && activeClass,\n isExactActive && exactActiveClass,\n ].filter(Boolean).join(' ') || undefined;\n };\n\n return h('a', {\n href: safeHref,\n class: reactiveClass,\n onclick: (e) => {\n // Only intercept left-clicks without modifiers\n if (e.ctrlKey || e.metaKey || e.shiftKey || e.altKey || e.button !== 0) return;\n if (!target.shouldIntercept) return;\n e.preventDefault();\n navigate(target.navigateTo, { replace: rep, transition });\n },\n onmouseenter: shouldPrefetch ? () => prefetch(safeHref) : undefined,\n ...rest,\n }, ...(Array.isArray(children) ? children : [children]));\n}\n\n// --- NavLink with active states ---\n\nexport function NavLink(props) {\n return Link(props);\n}\n\n// --- Define Routes Helper ---\n// Creates route config from a flat object for convenience.\n\nexport function defineRoutes(config) {\n return Object.entries(config).map(([path, value]) => {\n if (typeof value === 'function') {\n return { path, component: value };\n }\n // Object form with layout, middleware, loading, error, etc.\n return { path, ...value };\n });\n}\n\n// --- Nested Route Helper ---\n\nexport function nestedRoutes(basePath, children, options = {}) {\n const { layout, loading, error } = options;\n\n return children.map(child => ({\n ...child,\n path: basePath + child.path,\n layout: child.layout || layout,\n loading: child.loading || loading,\n error: child.error || error,\n }));\n}\n\n// --- Route Groups ---\n// Group routes without affecting URL structure\n\nexport function routeGroup(name, routes, options = {}) {\n const { layout, middleware } = options;\n\n return routes.map(route => ({\n ...route,\n _group: name,\n layout: route.layout || layout,\n middleware: [...(route.middleware || []), ...(middleware || [])],\n }));\n}\n\n// --- Redirect ---\n\nexport function Redirect({ to }) {\n navigate(to, { replace: true });\n return null;\n}\n\n// --- Route Guards / Middleware ---\n\nexport function guard(check, fallback) {\n return (Component) => {\n return function GuardedRoute(props) {\n const result = check(props);\n\n // Support async guards\n if (result instanceof Promise) {\n // Return loading while checking\n return h('div', { class: 'what-guard-loading' }, 'Loading...');\n }\n\n if (result) {\n return h(Component, props);\n }\n\n if (typeof fallback === 'string') {\n navigate(fallback, { replace: true });\n return null;\n }\n return h(fallback, props);\n };\n };\n}\n\n// Async guard with suspense\nexport function asyncGuard(check, options = {}) {\n const { fallback = '/login', loading = null } = options;\n\n return (Component) => {\n return function AsyncGuardedRoute(props) {\n const status = signal('pending');\n const checkResult = signal(null);\n let cancelled = false;\n\n effect(() => {\n cancelled = false;\n Promise.resolve(check(props))\n .then(result => {\n if (cancelled) return;\n checkResult.set(result);\n status.set(result ? 'allowed' : 'denied');\n })\n .catch(() => {\n if (!cancelled) status.set('denied');\n });\n return () => { cancelled = true; };\n });\n\n // Return a reactive function child so status changes update the DOM.\n // Components run once, so reading status() outside a reactive wrapper\n // would snapshot the value and never update.\n return () => {\n const currentStatus = status();\n\n if (currentStatus === 'pending') {\n return loading ? h(loading, {}) : null;\n }\n\n if (currentStatus === 'allowed') {\n return h(Component, props);\n }\n\n if (typeof fallback === 'string') {\n navigate(fallback, { replace: true });\n return null;\n }\n return h(fallback, props);\n };\n };\n };\n}\n\n// --- Prefetch ---\n// Hint the browser to prefetch a route's assets.\n\nconst prefetchedUrls = new Set();\n\nexport function prefetch(href) {\n if (typeof document === 'undefined') return;\n if (!isSafeUrl(href)) {\n if (typeof console !== 'undefined') {\n console.warn(`[what-router] Blocked prefetch for unsafe URL: ${href}`);\n }\n return;\n }\n if (prefetchedUrls.has(href)) return;\n prefetchedUrls.add(href);\n\n const link = document.createElement('link');\n link.rel = 'prefetch';\n link.href = href;\n document.head.appendChild(link);\n}\n\n// --- Scroll Restoration ---\n\nconst scrollPositions = new Map();\n\nexport function enableScrollRestoration() {\n if (typeof window === 'undefined') return;\n\n // Save scroll position before navigation\n window.addEventListener('beforeunload', () => {\n scrollPositions.set(location.pathname, window.scrollY);\n });\n\n // Restore scroll position after navigation\n effect(() => {\n const path = route.path;\n const savedPosition = scrollPositions.get(path);\n\n requestAnimationFrame(() => {\n if (savedPosition !== undefined) {\n window.scrollTo(0, savedPosition);\n } else if (route.hash) {\n const el = document.querySelector(route.hash);\n el?.scrollIntoView();\n } else {\n window.scrollTo(0, 0);\n }\n });\n });\n}\n\n// --- View Transition Helpers ---\n\nexport function viewTransitionName(name) {\n return { style: { viewTransitionName: name } };\n}\n\n// Configure view transition types\nexport function setViewTransition(type) {\n if (typeof document === 'undefined') return;\n document.documentElement.dataset.transition = type;\n}\n\n// --- useRoute Hook ---\n\nexport function useRoute() {\n return {\n path: computed(() => route.path),\n params: computed(() => route.params),\n query: computed(() => route.query),\n hash: computed(() => route.hash),\n isNavigating: computed(() => route.isNavigating),\n navigate,\n prefetch,\n };\n}\n\n// --- Outlet Component ---\n// For nested route rendering\n\nexport function Outlet({ children }) {\n // Children passed from parent layout\n return children || null;\n}\n\n// --- File-Based Router ---\n// Consumes routes generated by what-compiler's file router (virtual:what-routes).\n// Usage:\n// import { routes } from 'virtual:what-routes';\n// mount(<FileRouter routes={routes} />, '#app');\n\nexport function FileRouter({\n routes,\n layout: globalLayout,\n fallback,\n error: globalError,\n}) {\n // Convert file-router route format to Router's expected format\n const routerRoutes = routes.map(r => ({\n path: r.path,\n component: r.component,\n layout: r.layout || undefined,\n // Attach page mode as metadata for build system\n _mode: r.mode || 'client',\n }));\n\n // Router already returns a reactive function child \u2014 just delegate\n return Router({\n routes: routerRoutes,\n globalLayout,\n fallback: fallback || Default404,\n });\n}\n\nfunction Default404() {\n return h('div', { style: 'text-align:center;padding:60px 20px' },\n h('h1', { style: 'font-size:48px;margin-bottom:8px' }, '404'),\n h('p', { style: 'color:#64748b' }, 'Page not found'),\n );\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["signal", "effect", "computed", "batch", "h", "ErrorBoundary", "isSafeUrl", "url", "normalized", "safeDecodeURIComponent", "value", "getLinkTarget", "href", "fallback", "_url", "_params", "_query", "_isNavigating", "_navigationError", "route", "navigate", "to", "opts", "replace", "state", "transition", "_fromPopstate", "newUrl", "el", "doNavigation", "scrollPositions", "e", "saved", "compilePath", "path", "_", "name", "paramNames", "catchAll", "regexStr", "segment", "matchRoute", "routes", "sorted", "r", "a", "b", "aSpecific", "bSpecific", "regex", "match", "params", "i", "parseQuery", "search", "qs", "pair", "key", "val", "decodedKey", "decodedVal", "buildLayoutChain", "layouts", "segments", "currentPath", "layoutRoute", "_redirectHistory", "MAX_REDIRECTS", "Router", "globalLayout", "currentUrl", "isNavigating", "matched", "queryObj", "mw", "result", "cycle", "seen", "hasCycle", "element", "Layout", "Link", "cls", "className", "children", "rep", "shouldPrefetch", "activeClass", "exactActiveClass", "rest", "target", "safeHref", "hrefPath", "isActive", "isExactActive", "prefetch", "NavLink", "props", "defineRoutes", "config", "nestedRoutes", "basePath", "options", "layout", "loading", "error", "child", "routeGroup", "middleware", "Redirect", "guard", "check", "Component", "asyncGuard", "status", "checkResult", "cancelled", "currentStatus", "prefetchedUrls", "link", "enableScrollRestoration", "savedPosition", "viewTransitionName", "setViewTransition", "type", "useRoute", "Outlet", "FileRouter", "globalError", "routerRoutes", "Default404"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "what-router",
3
- "version": "0.6.5",
3
+ "version": "0.6.7",
4
4
  "description": "What Framework - File-based & programmatic router with View Transitions",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -28,7 +28,7 @@
28
28
  "author": "ZVN DEV (https://zvndev.com)",
29
29
  "license": "MIT",
30
30
  "peerDependencies": {
31
- "what-core": "^0.6.5"
31
+ "what-core": "^0.6.8"
32
32
  },
33
33
  "repository": {
34
34
  "type": "git",
package/src/index.js CHANGED
@@ -18,6 +18,39 @@ export function isSafeUrl(url) {
18
18
  return true;
19
19
  }
20
20
 
21
+ function safeDecodeURIComponent(value) {
22
+ try {
23
+ return decodeURIComponent(value);
24
+ } catch {
25
+ return value;
26
+ }
27
+ }
28
+
29
+ function getLinkTarget(href) {
30
+ const fallback = { href, navigateTo: href, shouldIntercept: false, isSafe: false };
31
+ if (!isSafeUrl(href)) return fallback;
32
+
33
+ if (typeof window === 'undefined') {
34
+ return { href, navigateTo: href, shouldIntercept: true, isSafe: true };
35
+ }
36
+
37
+ try {
38
+ const url = new URL(href, window.location.href);
39
+ const isHttp = url.protocol === 'http:' || url.protocol === 'https:';
40
+ if (!isHttp || url.origin !== window.location.origin) {
41
+ return { href, navigateTo: href, shouldIntercept: false, isSafe: true };
42
+ }
43
+ return {
44
+ href,
45
+ navigateTo: url.pathname + url.search + url.hash,
46
+ shouldIntercept: true,
47
+ isSafe: true,
48
+ };
49
+ } catch {
50
+ return { href, navigateTo: href, shouldIntercept: false, isSafe: true };
51
+ }
52
+ }
53
+
21
54
  // --- Route State (global singleton) ---
22
55
 
23
56
  const _url = signal(typeof location !== 'undefined' ? location.pathname + location.search + location.hash : '/');
@@ -59,7 +92,12 @@ export async function navigate(to, opts = {}) {
59
92
  const newUrl = basePath + to;
60
93
  history.replaceState(state, '', newUrl);
61
94
  _url.set(newUrl);
62
- const el = document.querySelector(to);
95
+ let el = null;
96
+ try {
97
+ el = document.getElementById(decodeURIComponent(to.slice(1)));
98
+ } catch {
99
+ el = document.getElementById(to.slice(1));
100
+ }
63
101
  if (el) el.scrollIntoView({ behavior: 'smooth' });
64
102
  return;
65
103
  }
@@ -87,18 +125,25 @@ export async function navigate(to, opts = {}) {
87
125
  }
88
126
  }
89
127
  _url.set(to);
90
- _isNavigating.set(false);
91
128
  };
92
129
 
93
- // Use View Transitions API if available and enabled
94
- if (transition && typeof document !== 'undefined' && document.startViewTransition) {
95
- try {
96
- await document.startViewTransition(doNavigation).finished;
97
- } catch (e) {
98
- // Transition failed, navigation still happened
130
+ try {
131
+ // Use View Transitions API if available and enabled
132
+ if (transition && typeof document !== 'undefined' && document.startViewTransition) {
133
+ try {
134
+ await document.startViewTransition(doNavigation).finished;
135
+ } catch (e) {
136
+ // Transition failed; fall back to direct navigation if it did not run.
137
+ if (_url.peek() !== to) doNavigation();
138
+ }
139
+ } else {
140
+ doNavigation();
99
141
  }
100
- } else {
101
- doNavigation();
142
+ } catch (e) {
143
+ _navigationError.set(e);
144
+ throw e;
145
+ } finally {
146
+ _isNavigating.set(false);
102
147
  }
103
148
  }
104
149
 
@@ -179,7 +224,7 @@ function matchRoute(path, routes) {
179
224
  if (match) {
180
225
  const params = {};
181
226
  paramNames.forEach((name, i) => {
182
- params[name] = decodeURIComponent(match[i + 1]);
227
+ params[name] = safeDecodeURIComponent(match[i + 1]);
183
228
  });
184
229
  return { route, params };
185
230
  }
@@ -194,8 +239,8 @@ function parseQuery(search) {
194
239
  for (const pair of qs.split('&')) {
195
240
  const [key, val] = pair.split('=');
196
241
  if (!key) continue;
197
- const decodedKey = decodeURIComponent(key);
198
- const decodedVal = val ? decodeURIComponent(val) : '';
242
+ const decodedKey = safeDecodeURIComponent(key);
243
+ const decodedVal = val ? safeDecodeURIComponent(val) : '';
199
244
  if (decodedKey in params) {
200
245
  // Collect repeated keys into arrays
201
246
  if (Array.isArray(params[decodedKey])) {
@@ -371,23 +416,26 @@ export function Link({
371
416
  transition = true,
372
417
  ...rest
373
418
  }) {
374
- // Sanitize href — reject dangerous protocols
375
- const safeHref = isSafeUrl(href) ? href : 'about:blank';
376
- if (!isSafeUrl(href) && typeof console !== 'undefined') {
419
+ // Sanitize href — reject dangerous protocols and only intercept same-origin routes.
420
+ const target = getLinkTarget(href);
421
+ const safeHref = target.isSafe ? href : 'about:blank';
422
+ if (!target.isSafe && typeof console !== 'undefined') {
377
423
  console.warn(`[what-router] Link blocked unsafe href: ${href}`);
378
424
  }
379
425
 
380
- // Strip query string and hash from href for path comparison
381
- const hrefPath = safeHref.split('?')[0].split('#')[0];
426
+ // Strip query string and hash from same-origin targets for path comparison.
427
+ const hrefPath = target.shouldIntercept
428
+ ? target.navigateTo.split('?')[0].split('#')[0]
429
+ : '';
382
430
 
383
431
  // Use a reactive function for class so active states update on navigation.
384
432
  // In the run-once model, reading route.path directly would snapshot it.
385
433
  const reactiveClass = () => {
386
434
  const currentPath = route.path;
387
- const isActive = hrefPath === '/'
435
+ const isActive = target.shouldIntercept && (hrefPath === '/'
388
436
  ? currentPath === '/'
389
- : currentPath === hrefPath || currentPath.startsWith(hrefPath + '/');
390
- const isExactActive = currentPath === hrefPath;
437
+ : currentPath === hrefPath || currentPath.startsWith(hrefPath + '/'));
438
+ const isExactActive = target.shouldIntercept && currentPath === hrefPath;
391
439
 
392
440
  return [
393
441
  cls || className,
@@ -402,8 +450,9 @@ export function Link({
402
450
  onclick: (e) => {
403
451
  // Only intercept left-clicks without modifiers
404
452
  if (e.ctrlKey || e.metaKey || e.shiftKey || e.altKey || e.button !== 0) return;
453
+ if (!target.shouldIntercept) return;
405
454
  e.preventDefault();
406
- navigate(safeHref, { replace: rep, transition });
455
+ navigate(target.navigateTo, { replace: rep, transition });
407
456
  },
408
457
  onmouseenter: shouldPrefetch ? () => prefetch(safeHref) : undefined,
409
458
  ...rest,
@@ -545,6 +594,12 @@ const prefetchedUrls = new Set();
545
594
 
546
595
  export function prefetch(href) {
547
596
  if (typeof document === 'undefined') return;
597
+ if (!isSafeUrl(href)) {
598
+ if (typeof console !== 'undefined') {
599
+ console.warn(`[what-router] Blocked prefetch for unsafe URL: ${href}`);
600
+ }
601
+ return;
602
+ }
548
603
  if (prefetchedUrls.has(href)) return;
549
604
  prefetchedUrls.add(href);
550
605