@vielzeug/wayfinder 3.0.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/compile.cjs +1 -1
- package/dist/compile.cjs.map +1 -1
- package/dist/compile.js +1 -1
- package/dist/history.cjs +1 -1
- package/dist/history.cjs.map +1 -1
- package/dist/history.d.ts +4 -0
- package/dist/history.d.ts.map +1 -1
- package/dist/history.js +35 -3
- package/dist/history.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/router.cjs +1 -1
- package/dist/router.js +27 -27
- package/dist/wayfinder.cjs +1 -1
- package/dist/wayfinder.cjs.map +1 -1
- package/dist/wayfinder.iife.js +1 -1
- package/dist/wayfinder.iife.js.map +1 -1
- package/dist/wayfinder.js +1 -1
- package/dist/wayfinder.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wayfinder.iife.js","names":[],"sources":["../src/errors.ts","../src/history.ts","../src/middleware.ts","../src/_navigation.ts","../src/_dev.ts","../src/path.ts","../src/compile.ts","../src/context.ts","../src/guards.ts","../src/router.ts"],"sourcesContent":["/** Base class for all wayfinder errors. Use `instanceof WayfinderError` to catch any router-originated error. */\nexport class WayfinderError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when any router method is called after the router has been disposed. */\nexport class WayfinderDisposedError extends WayfinderError {\n constructor() {\n super('Router is disposed');\n }\n}\n\n/** Thrown when a route or path definition is malformed. */\nexport class WayfinderRouteError extends WayfinderError {}\n\n/** Thrown when the router detects an infinite redirect loop. */\nexport class WayfinderRedirectLoopError extends WayfinderError {\n constructor() {\n super('Redirect loop detected');\n }\n}\n\n/** Thrown on API misuse (e.g. calling next() multiple times). */\nexport class WayfinderApiError extends WayfinderError {}\n","import type { HistoryDriver } from './types';\n\n/** Creates a history driver backed by the browser History API. */\nexport function createBrowserHistory(): HistoryDriver {\n return {\n back() {\n window.history.back();\n },\n get location() {\n return {\n hash: window.location.hash,\n pathname: window.location.pathname,\n search: window.location.search,\n // State lives on window.history.state, not window.location.\n state: window.history.state,\n };\n },\n onPopstate(listener) {\n window.addEventListener('popstate', listener);\n\n return () => window.removeEventListener('popstate', listener);\n },\n push(url, state) {\n window.history.pushState(state, '', url);\n },\n replace(url, state) {\n window.history.replaceState(state, '', url);\n },\n };\n}\n\ntype MemoryLocation = { hash: string; pathname: string; search: string; state: unknown };\n\n/**\n * Creates an in-memory history driver. Suitable for SSR, tests, and non-browser environments.\n *\n * Semantics mirror the browser History API:\n * - `push()` and `replace()` update the location silently (no listener notification),\n * matching `pushState`/`replaceState` which do not fire `popstate`.\n * - `back()` moves one entry back and notifies subscribers, matching a browser back-button press.\n */\nexport function createMemoryHistory(initialPath = '/'): HistoryDriver {\n const parsed = new URL(initialPath, 'http://localhost');\n const stack: MemoryLocation[] = [\n { hash: parsed.hash, pathname: parsed.pathname, search: parsed.search, state: null },\n ];\n let cursor = 0;\n const listeners = new Set<() => void>();\n\n return {\n back() {\n if (cursor <= 0) return;\n\n cursor -= 1;\n listeners.forEach((l) => {\n l();\n });\n },\n get location(): MemoryLocation {\n return stack[cursor]!;\n },\n onPopstate(listener) {\n listeners.add(listener);\n\n return () => listeners.delete(listener);\n },\n push(url, state = null) {\n const p = new URL(url, 'http://localhost');\n\n stack.splice(cursor + 1);\n stack.push({ hash: p.hash, pathname: p.pathname, search: p.search, state });\n cursor = stack.length - 1;\n // Silent — mirrors browser pushState semantics. Router drives navigation via #handleRoute directly.\n },\n replace(url, state = null) {\n const p = new URL(url, 'http://localhost');\n\n stack[cursor] = { hash: p.hash, pathname: p.pathname, search: p.search, state };\n // Silent — mirrors browser replaceState semantics.\n },\n };\n}\n","import type { Middleware, NamedNavigationTarget, NavigateOptions, RawNavigationTarget, RouteTable } from './types';\n\n/**\n * Middleware helper that redirects navigation to another route.\n * Attach to a route's `middleware` array for declarative redirect patterns.\n *\n * Note: `next()` is intentionally not called — this middleware blocks the chain\n * and immediately navigates to the target.\n *\n * @example\n * // Redirect /old → /new\n * routes: {\n * old: { middleware: [redirectTo({ path: '/new' })], path: '/old' },\n * new: { path: '/new' },\n * }\n */\nexport function redirectTo<TRoutes extends RouteTable = RouteTable>(\n target: NamedNavigationTarget<TRoutes> | RawNavigationTarget | string,\n options?: NavigateOptions,\n): Middleware<TRoutes> {\n return async (context) => {\n await context.navigate(target, options);\n };\n}\n","export type NavigationAttempt = {\n isCurrent(): boolean;\n readonly signal: AbortSignal;\n};\n\n/** Coordinates navigation ownership so stale work cannot commit router state. */\nexport function createNavigationCoordinator() {\n let active: AbortController | undefined;\n\n return {\n begin(): NavigationAttempt {\n active?.abort();\n\n const controller = new AbortController();\n\n active = controller;\n\n return {\n isCurrent: () => active === controller && !controller.signal.aborted,\n signal: controller.signal,\n };\n },\n\n invalidate(reason?: unknown): void {\n active?.abort(reason);\n active = undefined;\n },\n };\n}\n","const isDev = !(globalThis as { __WAYFINDER_PROD__?: boolean }).__WAYFINDER_PROD__;\n\n/** @internal @security Messages may include user-supplied data. */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/wayfinder] ${msg}`);\n}\n\n/** @internal */\nexport function error(msg: string, ...args: unknown[]): void {\n if (isDev) console.error(`[@vielzeug/wayfinder] ${msg}`, ...args);\n}\n","import { WayfinderRouteError } from './errors';\nimport type { QueryParams, ResolvedQueryParams, RouteLocation, RouteMatcher, RouteParams, RouteRecord } from './types';\n\n/** Ensure leading slash, collapse duplicate slashes, preserve root. */\nexport function normalizePath(path: string): string {\n const normalized = `/${path}`.replace(/\\/+/g, '/');\n\n return normalized !== '/' && normalized.endsWith('/') ? normalized.slice(0, -1) : normalized;\n}\n\nexport function joinPaths(base: string, path: string): string {\n return normalizePath(`${base}/${path}`);\n}\n\nfunction escapeRegex(input: string): string {\n return input.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nexport function compilePathMatcher(path: string): RouteMatcher {\n const normalized = normalizePath(path);\n\n if (normalized === '/') {\n return {\n paramNames: [],\n pattern: /^\\/$/,\n prefixPattern: /^\\/$/,\n };\n }\n\n const paramNames: string[] = [];\n const segments = normalized.slice(1).split('/');\n const regexParts: string[] = ['^'];\n const prefixParts: string[] = ['^'];\n\n for (let i = 0; i < segments.length; i += 1) {\n const segment = segments[i]!;\n\n if (segment === '*') {\n if (i !== segments.length - 1) {\n throw new WayfinderRouteError(`Wildcard \"*\" must be the final segment in path: ${path}`);\n }\n\n regexParts.push('(?:/.*)?');\n break;\n }\n\n if (segment.startsWith(':') && segment.endsWith('*')) {\n if (i !== segments.length - 1) {\n throw new WayfinderRouteError(`Wildcard param must be final segment in path: ${path}`);\n }\n\n const name = segment.slice(1, -1);\n\n if (!/^\\w+$/.test(name)) {\n throw new WayfinderRouteError(\n `Invalid param name \":${name}\" in path \"${path}\". Param names must only contain word characters (a–z, A–Z, 0–9, _).`,\n );\n }\n\n paramNames.push(name);\n regexParts.push(i === segments.length - 1 ? '(?:/(.*))?' : '/(.*)');\n prefixParts.push('/.*');\n break;\n }\n\n if (segment.startsWith(':')) {\n const name = segment.slice(1);\n\n if (!/^\\w+$/.test(name)) {\n throw new WayfinderRouteError(\n `Invalid param name \":${name}\" in path \"${path}\". Param names must only contain word characters (a–z, A–Z, 0–9, _).`,\n );\n }\n\n paramNames.push(name);\n regexParts.push('/([^/]+)');\n prefixParts.push('/[^/]+');\n continue;\n }\n\n const escaped = escapeRegex(segment);\n\n regexParts.push(`/${escaped}`);\n prefixParts.push(`/${escaped}`);\n }\n\n regexParts.push('$');\n prefixParts.push('(?:/.*)?$');\n\n return {\n paramNames,\n pattern: new RegExp(regexParts.join('')),\n prefixPattern: new RegExp(prefixParts.join('')),\n };\n}\n\nexport function matchRecord(pathname: string, record: RouteRecord): RouteParams | null {\n const match = record.matcher.pattern.exec(pathname);\n\n if (!match) return null;\n\n const params: RouteParams = {};\n\n record.matcher.paramNames.forEach((name: string, index: number) => {\n const value = match[index + 1] ?? '';\n\n try {\n params[name] = decodeURIComponent(value);\n } catch {\n params[name] = value;\n }\n });\n\n return params;\n}\n\nexport function matchRouteFor(\n pathname: string,\n records: readonly RouteRecord[],\n): { params: RouteParams; record?: RouteRecord } {\n for (const record of records) {\n const params = matchRecord(pathname, record);\n\n if (params) return { params, record };\n }\n\n return { params: {} };\n}\n\nexport function matchesPrefix(pathname: string, record: RouteRecord): boolean {\n return record.matcher.prefixPattern.test(pathname);\n}\n\n/**\n * Parse `?foo=a&foo=b&bar=c` into `{ foo: ['a', 'b'], bar: 'c' }`.\n *\n * @security Query keys come directly from the (attacker-controlled) URL. Plain bracket\n * assignment (`out[key] = value`) would let a key like `__proto__` reach the inherited\n * `Object.prototype.__proto__` setter and silently rewrite `out`'s own prototype instead of\n * storing a value — `setQueryValue` uses `Object.defineProperty` so every key, including\n * `__proto__`/`constructor`/`prototype`, is always stored as a plain own data property.\n */\nexport function parseQuery(queryString: string): QueryParams {\n const search = new URLSearchParams(queryString);\n const out: QueryParams = {};\n\n const setQueryValue = (key: string, value: QueryParams[string]): void => {\n Object.defineProperty(out, key, { configurable: true, enumerable: true, value, writable: true });\n };\n\n for (const [key, value] of search.entries()) {\n const existing = Object.hasOwn(out, key) ? out[key] : undefined;\n\n if (existing === undefined) {\n setQueryValue(key, value);\n continue;\n }\n\n if (Array.isArray(existing)) {\n existing.push(value);\n continue;\n }\n\n setQueryValue(key, [existing, value]);\n }\n\n return out;\n}\n\n/** Build a URL from a path pattern, params, and query, respecting the router base. */\nexport function buildUrl(base: string, pattern: string, params: RouteParams = {}, query?: ResolvedQueryParams): string {\n let path = pattern.replace(/:(\\w+)(\\*)?/g, (_match, key: string, isWildcard: string) => {\n const value = params[key];\n\n if (value == null) throw new WayfinderRouteError(`Missing path param: ${key}`);\n\n if (isWildcard) {\n return value\n .split('/')\n .map((segment) => encodeURIComponent(segment))\n .join('/');\n }\n\n return encodeURIComponent(value);\n });\n\n if (query && Object.keys(query).length) {\n const search = new URLSearchParams();\n\n Object.entries(query).forEach(([key, value]) => {\n if (Array.isArray(value))\n value.forEach((item) => {\n search.append(key, String(item));\n });\n else search.set(key, String(value));\n });\n\n path += `?${search.toString()}`;\n }\n\n return joinPaths(base, path);\n}\n\n// ─── Location helpers (formerly in resolve.ts) ────────────────────────────────\n\nexport function stripBase(pathname: string, base = '/'): string {\n const normalizedPath = normalizePath(pathname);\n const normalizedBase = normalizePath(base);\n\n if (normalizedBase === '/') return normalizedPath;\n\n if (normalizedPath === normalizedBase) return '/';\n\n const prefix = `${normalizedBase}/`;\n\n return normalizedPath.startsWith(prefix)\n ? normalizePath(normalizedPath.slice(normalizedBase.length))\n : normalizedPath;\n}\n\nexport function readLocation(\n base: string,\n history: { location: { hash: string; pathname: string; search: string; state: unknown } },\n): RouteLocation {\n return {\n hash: history.location.hash.replace(/^#/, ''),\n historyState: history.location.state,\n pathname: stripBase(history.location.pathname || '/', base),\n query: parseQuery(history.location.search || ''),\n };\n}\n","import { warn } from './_dev';\nimport { WayfinderRouteError } from './errors';\nimport { compilePathMatcher, joinPaths, normalizePath } from './path';\nimport type { Middleware, RouteBranchDef, RouteDefinition, RouteRecord, RouterOptions, RouteTable } from './types';\n\nexport type CompiledRoutes = {\n records: readonly RouteRecord[];\n routesByName: ReadonlyMap<string, RouteRecord>;\n};\n\nexport function compileRoutes<TRoutes extends RouteTable>(options: RouterOptions<TRoutes>): CompiledRoutes {\n const records: RouteRecord[] = [];\n\n const compile = (\n name: string,\n route: RouteDefinition,\n ancestorPath: string,\n ancestorBranchDefs: RouteBranchDef[],\n ancestorMiddleware: RouteRecord['ownMiddleware'],\n ): void => {\n if (route.index && route.path !== undefined) {\n throw new WayfinderRouteError(`Route \"${name}\" cannot define both index and path`);\n }\n\n if (!route.index && route.path === undefined) {\n throw new WayfinderRouteError(`Route \"${name}\" must define path or set index: true`);\n }\n\n const ownPath = route.index\n ? ancestorPath\n : normalizePath(route.path ? joinPaths(ancestorPath, route.path) : ancestorPath);\n\n const branchDefs: RouteBranchDef[] = [\n ...ancestorBranchDefs,\n {\n dataFn: route.data,\n name,\n onError: route.onError as RouteBranchDef['onError'],\n },\n ];\n\n const ownMiddleware: Middleware[] = [\n ...ancestorMiddleware,\n ...((route.middleware ?? []) as unknown as Middleware[]),\n ];\n\n if (route.children) {\n for (const [childName, childRoute] of Object.entries(route.children)) {\n compile(`${name}.${childName}`, childRoute, ownPath, branchDefs, ownMiddleware);\n }\n }\n\n // Emit a record for leaf routes (no children) and routes with data or redirect.\n // A bare parent-only route (children but no data/redirect) is not emitted as a leaf.\n if (route.redirect !== undefined || !route.children || route.data !== undefined) {\n const leaf = branchDefs[branchDefs.length - 1]!;\n\n records.push({\n branchDefs,\n coerceSearch: route.coerceSearch,\n leaf,\n matcher: compilePathMatcher(ownPath),\n ownMiddleware,\n path: ownPath,\n redirect: route.redirect,\n });\n }\n };\n\n for (const [name, route] of Object.entries(options.routes)) {\n compile(name, route, '/', [], []);\n }\n\n // Validate duplicate names.\n const namesSeen = new Set<string>();\n\n for (const record of records) {\n if (namesSeen.has(record.leaf.name)) {\n throw new WayfinderRouteError(\n `Duplicate route name: \"${record.leaf.name}\". A top-level route key must not coincide with a nested route's dot-notation name.`,\n );\n }\n\n namesSeen.add(record.leaf.name);\n }\n\n // Warn when a catch-all or wildcard-param route precedes more specific routes.\n // Routes match in array order (object key order), so a wildcard placed too early silently shadows later routes.\n let wildcardSeen = false;\n\n for (const record of records) {\n const isCatchAll =\n record.path === '/*' || record.path.endsWith('/*') || (record.path.includes(':') && record.path.endsWith('*'));\n\n if (wildcardSeen && !isCatchAll) {\n warn(\n `Route \"${record.leaf.name}\" (${record.path}) is defined after a wildcard/catch-all route and will never match. Move specific routes before wildcards.`,\n );\n }\n\n if (isCatchAll) wildcardSeen = true;\n }\n\n return {\n records,\n routesByName: new Map(records.map((r) => [r.leaf.name, r])),\n };\n}\n","import { error as logError } from './_dev';\nimport { WayfinderApiError } from './errors';\nimport type {\n Middleware,\n NavigationStatus,\n ResolvedQueryParams,\n RouteBranchDef,\n RouteContext,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteParams,\n RouterErrorContext,\n RouteState,\n RouteTable,\n} from './types';\n\n// ─── Route state ──────────────────────────────────────────────────────────────\n\n/** No defensive deep-copy — RouteState is typed readonly; callers always pass fresh objects. */\nexport function createRouteState(input: {\n error?: unknown;\n location: RouteLocation;\n matches: RouteMatchBranch;\n status: NavigationStatus;\n}): RouteState {\n return {\n error: input.error,\n location: input.location,\n matches: input.matches,\n status: input.status,\n };\n}\n\n/**\n * Build a RouteMatchBranch from compiled defs and data results.\n */\nexport function buildMatchBranch(\n branchDefs: readonly RouteBranchDef[],\n params: RouteParams,\n pathname: string,\n dataResults: unknown[],\n): RouteMatchBranch {\n return branchDefs.map(\n (def, i): RouteMatch => ({\n data: dataResults[i],\n name: def.name,\n params: { ...params },\n pathname,\n }),\n );\n}\n\n// ─── Context factory ──────────────────────────────────────────────────────────\n\nexport function createRouteContext<TRoutes extends RouteTable>(\n location: RouteLocation,\n resolvedQuery: ResolvedQueryParams,\n params: RouteParams,\n matches: RouteMatchBranch,\n navigate: RouteContext<RouteParams, TRoutes>['navigate'],\n): RouteContext<RouteParams, TRoutes> {\n return {\n hash: location.hash,\n historyState: location.historyState,\n locals: {},\n matches,\n navigate,\n params,\n pathname: location.pathname,\n query: resolvedQuery,\n };\n}\n\n// ─── Middleware pipeline ──────────────────────────────────────────────────────\n\nexport async function executeMiddlewarePipeline<TRoutes extends RouteTable>(\n context: RouteContext<RouteParams, TRoutes>,\n middleware: readonly Middleware<TRoutes>[],\n terminal: () => Promise<void>,\n wrapMiddlewareError?: (error: unknown) => unknown,\n): Promise<boolean> {\n let terminalRan = false;\n\n async function dispatch(index: number): Promise<void> {\n if (index < middleware.length) {\n let called = false;\n\n try {\n await middleware[index]?.(context, async () => {\n if (called) throw new WayfinderApiError('next() called multiple times');\n\n called = true;\n await dispatch(index + 1);\n });\n } catch (error) {\n throw wrapMiddlewareError?.(error) ?? error;\n }\n\n return;\n }\n\n terminalRan = true;\n await terminal();\n }\n\n await dispatch(0);\n\n return terminalRan;\n}\n\n// ─── Error reporting ──────────────────────────────────────────────────────────\n\nexport function reportError(\n error: unknown,\n context: RouterErrorContext,\n onError?: (e: unknown, ctx: RouterErrorContext) => void,\n): void {\n if (onError) {\n onError(error, context);\n\n return;\n }\n\n logError('Unhandled router error. Provide an onError callback to handle errors explicitly:', error);\n}\n","import type { BeforeLeaveBlocker, NavigationDestination } from './types';\n\n/**\n * A registered leave guard. `routes` scopes the guard to navigations that depart from\n * any route whose name appears in the array (matched against any node in the active branch,\n * not just the leaf). When `routes` is omitted the guard fires on every navigation.\n */\nexport type RegisteredBlocker = {\n handler: BeforeLeaveBlocker;\n routes?: string[];\n};\n\n/**\n * Run all registered leave blockers in registration order.\n * Guards whose `routes` filter does not overlap the current active match names are skipped.\n * Returns `false` as soon as any blocker returns `false`; returns `true` if all pass.\n *\n * A snapshot of the blocker set is taken before iteration so that guards added or removed\n * during execution do not affect the current run.\n *\n * `destination` is passed to each blocker so guards can make context-aware decisions.\n */\nexport async function runLeaveBlockers(\n blockers: ReadonlySet<RegisteredBlocker>,\n activeMatchNames: readonly string[],\n destination: NavigationDestination,\n): Promise<boolean> {\n const activeNames = new Set(activeMatchNames);\n // Snapshot so that removals during iteration don't skip entries.\n const snapshot = [...blockers];\n\n for (const { handler, routes } of snapshot) {\n if (routes !== undefined && !routes.some((name) => activeNames.has(name))) continue;\n\n const allowed = await handler(destination);\n\n if (!allowed) return false;\n }\n\n return true;\n}\n","import { createNavigationCoordinator, type NavigationAttempt } from './_navigation';\nimport { compileRoutes } from './compile';\nimport {\n buildMatchBranch,\n createRouteContext,\n createRouteState,\n executeMiddlewarePipeline,\n reportError,\n} from './context';\nimport { WayfinderDisposedError, WayfinderRedirectLoopError, WayfinderRouteError } from './errors';\nimport { type RegisteredBlocker, runLeaveBlockers } from './guards';\nimport { createBrowserHistory } from './history';\nimport {\n buildUrl,\n joinPaths,\n matchesPrefix,\n matchRouteFor,\n normalizePath,\n parseQuery,\n readLocation,\n stripBase,\n} from './path';\nimport type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n HistoryDriver,\n IsActiveOptions,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n PathParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n RouteBranchDef,\n RouteContext,\n RouteLocation,\n RouteMatchBranch,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouteRecord,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n RouteViewMap,\n RouteViewName,\n RouteViewRegistry,\n Unsubscribe,\n} from './types';\n\n// ─── Module-level helpers ────────────────────────────────────────────────────\n\nfunction getRouteByName(name: string, routesByName: ReadonlyMap<string, RouteRecord>): RouteRecord {\n const route = routesByName.get(name);\n\n if (route) return route;\n\n const available = [...routesByName.keys()].join(', ');\n\n throw new WayfinderRouteError(\n available ? `Unknown route name: ${name}. Available routes: ${available}` : `Unknown route name: ${name}`,\n );\n}\n\nfunction resolveTarget(\n target: { path: string } | { hash?: string; name: string; params?: RouteParams; query?: ResolvedQueryParams },\n routesByName: ReadonlyMap<string, RouteRecord>,\n base = '/',\n): string {\n if ('path' in target) return target.path;\n\n const route = getRouteByName(target.name, routesByName);\n const path = buildUrl(base, route.path, target.params, target.query);\n\n return target.hash ? `${path}#${target.hash}` : path;\n}\n\n// ─── Internal error carrier ──────────────────────────────────────────────────\n//\n// Carries error context alongside the original error without mutating the thrown\n// object. The carrier is unwrapped before reaching external callers so error\n// identity and `cause` chains are preserved.\n\nconst ROUTER_ERROR = Symbol('wayfinder.routerError');\n\ntype RouterErrorCarrier = {\n [ROUTER_ERROR]: true;\n readonly error: unknown;\n readonly context: RouterErrorContext;\n};\n\nfunction carryError(error: unknown, context: RouterErrorContext): RouterErrorCarrier {\n return { [ROUTER_ERROR]: true, context, error };\n}\n\nfunction isRouterErrorCarrier(value: unknown): value is RouterErrorCarrier {\n return value !== null && typeof value === 'object' && ROUTER_ERROR in (value as Record<symbol, unknown>);\n}\n\nfunction unwrapCarrier(value: unknown): never {\n if (isRouterErrorCarrier(value)) throw value.error;\n\n throw value;\n}\n\n// ─── Internal types ───────────────────────────────────────────────────────────\n\ntype NavigationDecision =\n | {\n branch: RouteMatchBranch;\n location: RouteLocation;\n params: RouteParams;\n record: RouteRecord;\n resolvedQuery: ResolvedQueryParams;\n type: 'matched';\n }\n | { location: RouteLocation; params: RouteParams; type: 'unmatched' }\n | { location: RouteLocation; params: RouteParams; redirectTo: string; type: 'redirect' };\n\n// ─── Router class ─────────────────────────────────────────────────────────────\n\nclass Router<TRoutes extends RouteTable> {\n readonly #base: string;\n readonly #globalMiddleware: readonly Middleware[];\n readonly #globalCoerceSearch?: CoerceSearchFn;\n readonly #history: HistoryDriver;\n readonly #records: readonly RouteRecord[];\n readonly #routesByName: ReadonlyMap<string, RouteRecord>;\n readonly #onError?: RouterOptions<TRoutes>['onError'];\n readonly #scroll?: RouterOptions<TRoutes>['scroll'];\n readonly #useViewTransition: boolean;\n\n // Mutable navigation state\n readonly #beforeLeaveBlockers = new Set<RegisteredBlocker>();\n readonly #navigation = createNavigationCoordinator();\n #currentState: RouteState;\n #disposed = false;\n readonly #disposeController = new AbortController();\n #lastHref = '/';\n readonly #listeners = new Set<(state: RouteState) => void>();\n readonly #preloaded = new Map<string, unknown[]>();\n readonly #preloads = new Map<string, Promise<RouteState | null>>();\n // Compiled notFound fallback record\n readonly #notFoundRecord: RouteRecord | null;\n\n readonly #unlistenHistory: () => void;\n\n /** Resolves when the constructor-triggered navigation has settled; rejects if it fails. */\n readonly ready: Promise<void>;\n\n constructor(options: RouterOptions<TRoutes>) {\n const compiled = compileRoutes(options);\n\n this.#base = normalizePath(options.base ?? '/');\n this.#globalMiddleware = (options.middleware ?? []) as unknown as Middleware[];\n this.#globalCoerceSearch = options.coerceSearch;\n this.#history = options.history ?? createBrowserHistory();\n this.#onError = options.onError;\n this.#scroll = options.scroll;\n this.#useViewTransition = options.viewTransition ?? false;\n this.#records = compiled.records as unknown as readonly RouteRecord[];\n this.#routesByName = compiled.routesByName as unknown as ReadonlyMap<string, RouteRecord>;\n this.#currentState = createRouteState({\n location: { hash: '', historyState: null, pathname: '/', query: {} },\n matches: [] as RouteMatchBranch,\n status: 'idle',\n });\n\n // Build a synthetic RouteRecord for the notFound fallback.\n if (options.notFound) {\n const nf = options.notFound;\n const leafDef: RouteBranchDef = {\n dataFn: nf.data,\n name: '__notFound__',\n };\n\n this.#notFoundRecord = {\n branchDefs: [leafDef],\n leaf: leafDef,\n matcher: { paramNames: [], pattern: /(?:)/, prefixPattern: /(?:)/ },\n ownMiddleware: (nf.middleware ?? []) as unknown as Middleware[],\n path: '/*',\n };\n } else {\n this.#notFoundRecord = null;\n }\n\n this.#unlistenHistory = this.#registerHistoryListener();\n\n const { hash, pathname, search } = this.#history.location;\n\n this.#lastHref = `${pathname}${search}${hash}`;\n\n const attempt = this.#navigation.begin();\n\n const navigationPromise = this.#handleRoute(\n attempt,\n readLocation(this.#base, this.#history),\n (location, replace) => {\n if (!attempt.isCurrent()) return;\n\n const href = this.#hrefForLocation(location);\n\n if (replace) this.#history.replace(href, location.historyState);\n\n this.#lastHref = href;\n },\n undefined,\n 0,\n false,\n ).then(() => undefined);\n\n // ready rejects with the original error (carrier unwrapped) for external consumers.\n this.ready = navigationPromise.catch(unwrapCarrier);\n // Attach a silent handler so an unawaited ready promise doesn't trigger\n // an unhandled-rejection warning. External consumers can still await ready\n // and receive the rejection — each .catch() subscriber fires independently.\n this.ready.catch(() => {});\n // #runInBackground receives the raw promise so it can extract carrier context for onError.\n this.#runInBackground(navigationPromise, { source: 'initial-navigation' });\n\n // Router actions are intentionally bound once so they remain safe when destructured.\n this.beforeLeave = this.beforeLeave.bind(this);\n this.createViewRegistry = this.createViewRegistry.bind(this);\n this.dispose = this.dispose.bind(this);\n this.getSnapshot = this.getSnapshot.bind(this);\n this.isActive = this.isActive.bind(this);\n this.load = this.load.bind(this);\n this.match = this.match.bind(this);\n this.navigate = this.navigate.bind(this);\n this.preload = this.preload.bind(this);\n this.subscribe = this.subscribe.bind(this);\n this.url = this.url.bind(this);\n this.waitFor = this.waitFor.bind(this);\n }\n\n // ─── Public state ─────────────────────────────────────────────────────────\n\n /**\n * Returns the current immutable router state snapshot.\n *\n * Compatible with React's `useSyncExternalStore`:\n * ```ts\n * const state = useSyncExternalStore(\n * (cb) => router.subscribe(cb),\n * () => router.getSnapshot()\n * );\n * ```\n */\n getSnapshot(): RouteState {\n return this.#currentState;\n }\n\n createViewRegistry<const TViews extends RouteViewMap<TRoutes>, TNotFound = never>(\n views: TViews & Record<Exclude<keyof TViews, RouteViewName<TRoutes>>, never>,\n options?: { notFound?: TNotFound },\n ): RouteViewRegistry<TViews[keyof TViews] | TNotFound> {\n return {\n resolve: (state) => {\n const name = state.matches.at(-1)?.name;\n\n if (name === '__notFound__') return options?.notFound;\n\n return name ? (views[name as keyof TViews] as TViews[keyof TViews]) : undefined;\n },\n };\n }\n\n // ─── Subscription ─────────────────────────────────────────────────────────\n\n /**\n * Subscribe to state changes. The listener is called on each subsequent state change;\n * use `getSnapshot()` to read the current value immediately.\n */\n subscribe(listener: (state: RouteState) => void): Unsubscribe {\n this.#assertNotDisposed();\n this.#listeners.add(listener);\n\n return () => {\n this.#listeners.delete(listener);\n };\n }\n\n // ─── Navigation ───────────────────────────────────────────────────────────\n\n /**\n * Navigate using a named route target or a raw path target.\n *\n * If a data loader throws after terminal execution begins, history has already committed\n * the destination and the returned Promise rejects with the loader error.\n */\n navigate(\n target: NamedNavigationTarget<TRoutes> | RawNavigationTarget | string,\n options?: NavigateOptions,\n ): Promise<void> {\n const normalized = typeof target === 'string' ? { path: target } : target;\n const destination = resolveTarget(normalized, this.#routesByName);\n\n return this.#navigateToPath(destination, options);\n }\n\n // ─── URL helpers ──────────────────────────────────────────────────────────\n\n /** Build a URL for a named route, including optional params and query string. */\n url<Name extends RouteName<TRoutes>>(\n name: Name,\n params?: PathParams<RoutePathByName<TRoutes, Name>>,\n query?: ResolvedQueryParams,\n ): string {\n const route = getRouteByName(name, this.#routesByName);\n\n return buildUrl(this.#base, route.path, params, query);\n }\n\n /** Returns true when the current location matches the named route by prefix (default) or exactly. */\n isActive<Name extends RouteName<TRoutes>>(name: Name, options: IsActiveOptions = {}): boolean {\n const route = getRouteByName(name, this.#routesByName);\n const pathname = this.#currentState.location.pathname;\n const exact = options.exact ?? false;\n\n return exact ? matchRouteFor(pathname, [route]).record != null : matchesPrefix(pathname, route);\n }\n\n /** Match a pathname to a route branch without running middleware or data loaders. Returns null for redirects or no match. */\n match(pathname: string): RouteMatchBranch | null {\n const normalizedPathname = stripBase(normalizePath(pathname), this.#base);\n const { params, record } = matchRouteFor(normalizedPathname, this.#records);\n\n if (!record || record.redirect) return null;\n\n const branch = buildMatchBranch(\n record.branchDefs,\n params,\n normalizedPathname,\n record.branchDefs.map(() => undefined),\n );\n\n return branch.length ? branch : null;\n }\n\n /**\n * Load a URL into a route state including data loader results, without modifying\n * router state or history. Follows declarative redirects but does not cache results.\n * Middleware is not executed — use `navigate()` when middleware side effects are needed.\n */\n async load(url: string, options?: { signal?: AbortSignal }): Promise<RouteState | null> {\n const prepared = await this.#resolveUrl(url);\n\n if (prepared.type !== 'matched') return null;\n\n const { location, params, record, resolvedQuery } = prepared;\n const defs = record.branchDefs;\n const hasData = defs.some((d) => d.dataFn != null);\n let dataResults: unknown[] = defs.map(() => undefined);\n let error: unknown;\n let status: 'error' | 'idle' = 'idle';\n\n if (hasData) {\n const effectiveSignal = options?.signal ?? new AbortController().signal;\n const branch = buildMatchBranch(defs, params, location.pathname, dataResults);\n const context = createRouteContext<TRoutes>(location, resolvedQuery, params, branch, () => Promise.resolve());\n\n try {\n dataResults = await this.#loadData(defs, context, effectiveSignal);\n } catch (e) {\n error = e;\n status = 'error';\n }\n }\n\n return createRouteState({\n error,\n location,\n matches: buildMatchBranch(defs, params, location.pathname, dataResults),\n status,\n });\n }\n\n async preload(target: NamedNavigationTarget<TRoutes>): Promise<RouteState | null> {\n this.#assertNotDisposed();\n\n const destination = this.#resolveDestination(resolveTarget(target, this.#routesByName, this.#base));\n const existing = this.#preloads.get(destination);\n\n if (existing) return existing;\n\n const work = this.load(destination, { signal: this.#disposeController.signal })\n .then((state) => {\n if (this.#disposed) throw this.#disposeController.signal.reason;\n\n if (state?.status === 'error') throw state.error;\n\n if (state) {\n const key = this.#hrefForLocation(state.location);\n\n if (this.#preloaded.size >= 20) this.#preloaded.delete(this.#preloaded.keys().next().value!);\n\n this.#preloaded.set(\n key,\n state.matches.map((match) => match.data),\n );\n }\n\n return state;\n })\n .catch((error) => {\n if (this.#onError) this.#reportError(error, { source: 'preload' });\n\n throw error;\n })\n .finally(() => {\n this.#preloads.delete(destination);\n });\n\n this.#preloads.set(destination, work);\n\n return work;\n }\n\n /**\n * Returns a Promise that resolves the next time the router reaches `status: 'idle'`\n * and the active matches include a route named `name`.\n *\n * - Resolves immediately if the router is already `idle` at `name`.\n * - Rejects immediately if the router is already in `status: 'error'`.\n * - If the router is currently `idle` at a **different** route and no navigation is in\n * flight, this promise will not resolve until a future navigation lands on `name`.\n * Typical use-case is awaiting a navigation you just triggered:\n * `router.navigate(target); await router.waitFor('routeName')`.\n * @throws {WayfinderDisposedError} if the router is disposed while the promise is pending,\n * or if called after the router has already been disposed.\n */\n waitFor(name: RouteName<TRoutes>): Promise<RouteState> {\n this.#assertNotDisposed();\n\n return new Promise((resolve, reject) => {\n const matchesName = (state: RouteState): boolean =>\n state.status === 'idle' && state.matches.some((m) => m.name === name);\n\n if (this.#currentState.status === 'error') {\n reject(this.#currentState.error);\n\n return;\n }\n\n if (matchesName(this.#currentState)) {\n resolve(this.#currentState);\n\n return;\n }\n\n const unsub = this.subscribe((state) => {\n if (matchesName(state)) {\n unsub();\n resolve(state);\n } else if (state.status === 'error') {\n unsub();\n reject(state.error);\n }\n });\n\n this.#disposeController.signal.addEventListener(\n 'abort',\n () => {\n unsub();\n reject(this.#disposeController.signal.reason);\n },\n { once: true },\n );\n });\n }\n\n // ─── Navigation guards ────────────────────────────────────────────────────\n\n /**\n * Register a leave guard. Called before user-triggered navigation attempts.\n * Return `false` to cancel; `true` to allow.\n * Returns a function that removes the guard.\n *\n * Use `options.routes` to scope the guard to specific routes (fires only when navigating\n * away from a route whose name appears in the array, checked against any node in the active branch).\n */\n beforeLeave(blocker: BeforeLeaveBlocker, options?: BeforeLeaveOptions<TRoutes>): Unsubscribe {\n this.#assertNotDisposed();\n\n const entry: RegisteredBlocker = {\n handler: blocker,\n routes: options?.routes as string[] | undefined,\n };\n\n this.#beforeLeaveBlockers.add(entry);\n\n return () => {\n this.#beforeLeaveBlockers.delete(entry);\n };\n }\n\n // ─── Lifecycle ────────────────────────────────────────────────────────────\n\n get disposalSignal(): AbortSignal {\n return this.#disposeController.signal;\n }\n\n get disposed(): boolean {\n return this.#disposed;\n }\n\n /** Dispose event listeners and prevent further router interaction. Idempotent. */\n dispose(): void {\n if (this.#disposed) return;\n\n this.#disposed = true;\n this.#beforeLeaveBlockers.clear();\n this.#listeners.clear();\n this.#preloaded.clear();\n this.#preloads.clear();\n this.#navigation.invalidate(new WayfinderDisposedError());\n this.#unlistenHistory();\n // Abort the disposal signal last — waitFor() listeners clean themselves up via this signal.\n this.#disposeController.abort(new WayfinderDisposedError());\n }\n\n [Symbol.dispose](): void {\n this.dispose();\n }\n\n // ─── Private: assertions ──────────────────────────────────────────────────\n\n #assertNotDisposed(): void {\n if (this.#disposed) throw new WayfinderDisposedError();\n }\n\n // ─── Private: history listener ────────────────────────────────────────────\n\n #registerHistoryListener(): () => void {\n return this.#history.onPopstate(() => {\n const { hash, pathname, search } = this.#history.location;\n const newHref = `${pathname}${search}${hash}`;\n const previousHref = this.#lastHref;\n\n if (newHref === previousHref) return;\n\n this.#runInBackground(this.#handleHistoryNavigation(newHref, previousHref), { source: 'history-listener' });\n });\n }\n\n async #handleHistoryNavigation(newHref: string, previousHref: string): Promise<void> {\n const attempt = this.#navigation.begin();\n const activeMatchNames = this.#currentState.matches.map((m) => m.name);\n const destination = this.#navigationDestination(newHref);\n const allowed = await runLeaveBlockers(this.#beforeLeaveBlockers, activeMatchNames, destination);\n\n if (!attempt.isCurrent()) return;\n\n if (!allowed) {\n this.#history.replace(previousHref, this.#currentState.location.historyState);\n\n return;\n }\n\n const terminalRan = await this.#handleRoute(\n attempt,\n readLocation(this.#base, this.#history),\n (location, replace) => {\n if (!attempt.isCurrent()) return;\n\n const href = this.#hrefForLocation(location);\n\n if (replace) this.#history.replace(href, location.historyState);\n\n this.#lastHref = href;\n },\n undefined,\n 0,\n false,\n );\n\n if (attempt.isCurrent() && !terminalRan) {\n this.#history.replace(previousHref, this.#currentState.location.historyState);\n }\n }\n\n // ─── Private: error handling ──────────────────────────────────────────────\n\n #reportError(error: unknown, context: RouterErrorContext): void {\n reportError(error, context, this.#onError);\n }\n\n #runInBackground(promise: Promise<void>, context: RouterErrorContext): void {\n void promise.catch((value) => {\n if (isRouterErrorCarrier(value)) {\n this.#reportError(value.error, value.context);\n } else {\n this.#reportError(value, context);\n }\n });\n }\n\n // ─── Private: listener notification ──────────────────────────────────────\n\n #notifyListeners(): void {\n this.#listeners.forEach((listener) => {\n listener(this.#currentState);\n });\n }\n\n // ─── Private: data loaders ────────────────────────────────────────────────\n\n /**\n * Run all data loaders and return their results. Per-def `onError` boundaries are applied.\n */\n async #loadData(\n defs: readonly RouteBranchDef[],\n context: RouteContext<RouteParams, TRoutes>,\n signal: AbortSignal,\n ): Promise<unknown[]> {\n return Promise.all(\n defs.map(async (def) => {\n if (!def.dataFn) return undefined;\n\n const dataFn = def.dataFn as unknown as DataFn<RouteParams, TRoutes>;\n\n try {\n return await dataFn({ ...context, signal } as DataContext<RouteParams, TRoutes>);\n } catch (err) {\n if (def.onError) return def.onError(err, { ...context, signal } as unknown as DataContext);\n\n throw err;\n }\n }),\n );\n }\n\n // ─── Private: URL resolution ──────────────────────────────────────────────\n\n /** Parse a URL, call #prepareRoute, and follow declarative redirects up to 5 hops. */\n async #resolveUrl(url: string): Promise<NavigationDecision> {\n let destination = url;\n\n for (let i = 0; i < 5; i += 1) {\n const parsed = new URL(destination, 'http://localhost');\n const location: RouteLocation = {\n hash: parsed.hash.replace(/^#/, ''),\n historyState: null,\n pathname: stripBase(parsed.pathname, this.#base),\n query: parseQuery(parsed.search),\n };\n\n const prepared = await this.#prepareRoute(location);\n\n if (prepared.type !== 'redirect') return prepared;\n\n destination = this.#resolveDestination(prepared.redirectTo);\n }\n\n throw new WayfinderRedirectLoopError();\n }\n\n // ─── Private: route preparation ───────────────────────────────────────────\n\n async #prepareRoute(location: RouteLocation): Promise<NavigationDecision> {\n const { params, record } = matchRouteFor(location.pathname, this.#records);\n\n if (!record) {\n return { location, params, type: 'unmatched' };\n }\n\n if (record.redirect) {\n return {\n location,\n params,\n redirectTo: resolveTarget(record.redirect, this.#routesByName, this.#base),\n type: 'redirect',\n };\n }\n\n let resolvedQuery: ResolvedQueryParams = location.query;\n\n const coerce: CoerceSearchFn | undefined = record.coerceSearch ?? this.#globalCoerceSearch;\n\n if (coerce) {\n try {\n resolvedQuery = coerce(location.query);\n } catch (err) {\n this.#reportError(err, { source: 'coerce-search' });\n resolvedQuery = location.query;\n }\n }\n\n return {\n branch: buildMatchBranch(\n record.branchDefs,\n params,\n location.pathname,\n record.branchDefs.map(() => undefined),\n ),\n location,\n params,\n record,\n resolvedQuery,\n type: 'matched',\n };\n }\n\n // ─── Private: terminal (data only) ────────────────────────────────────────\n\n async #runTerminal(\n record: RouteRecord,\n context: RouteContext<RouteParams, TRoutes>,\n location: RouteLocation,\n params: RouteParams,\n signal: AbortSignal,\n isCurrent: () => boolean,\n onDataError?: (error: unknown) => void,\n ): Promise<void> {\n if (!isCurrent()) return;\n\n const defs = record.branchDefs;\n const hasData = defs.some((d) => d.dataFn != null);\n let dataResults: unknown[] = defs.map(() => undefined);\n\n if (hasData) {\n const preloadKey = this.#hrefForLocation(location);\n const preloading = this.#preloads.get(preloadKey);\n\n if (preloading) {\n await preloading.catch(() => undefined);\n\n if (!isCurrent()) return;\n }\n\n const cached = this.#preloaded.get(preloadKey);\n\n if (cached) {\n this.#preloaded.delete(preloadKey);\n dataResults = cached;\n } else {\n this.#currentState = createRouteState({\n location,\n matches: buildMatchBranch(defs, params, location.pathname, dataResults),\n status: 'loading',\n });\n this.#notifyListeners();\n\n try {\n dataResults = await this.#loadData(defs, context, signal);\n } catch (error) {\n if (!isCurrent()) return;\n\n this.#currentState = createRouteState({\n error,\n location,\n matches: buildMatchBranch(defs, params, location.pathname, dataResults),\n status: 'error',\n });\n onDataError?.(error);\n throw error;\n }\n }\n }\n\n if (!isCurrent()) return;\n\n this.#currentState = createRouteState({\n location,\n matches: buildMatchBranch(defs, params, location.pathname, dataResults),\n status: 'idle',\n });\n }\n\n // ─── Private: main navigation orchestrator ────────────────────────────────\n\n /**\n * Declarative redirects reuse the active attempt and replace the final history entry.\n * Middleware redirects call `ctx.navigate()`, which starts a new attempt.\n */\n async #handleRoute(\n attempt: NavigationAttempt,\n currentLocation: RouteLocation,\n commit: (location: RouteLocation, replace: boolean) => void,\n useTransition?: boolean,\n depth = 0,\n replace = false,\n ): Promise<boolean> {\n const previousState = this.#currentState;\n const isCurrent = (): boolean => attempt.isCurrent() && !this.#disposed;\n const prepared = await this.#prepareRoute(currentLocation);\n\n if (!isCurrent()) return false;\n\n if (prepared.type === 'redirect') {\n if (depth >= 5) throw new WayfinderRedirectLoopError();\n\n return this.#handleRoute(\n attempt,\n this.#locationFromPath(prepared.redirectTo, currentLocation.historyState),\n commit,\n useTransition,\n depth + 1,\n true,\n );\n }\n\n if (prepared.type === 'unmatched') {\n // Fall back to notFound record when defined.\n if (this.#notFoundRecord) {\n const nfDefs = [this.#notFoundRecord.leaf];\n const nfBranch = buildMatchBranch(nfDefs, {}, currentLocation.pathname, [undefined]);\n let committed = false;\n let dataError: unknown;\n let hasDataError = false;\n let terminalRan = false;\n\n // Apply global coerceSearch to the unmatched location so notFound handlers\n // receive typed query params, consistent with matched-route behaviour.\n let nfResolvedQuery: ResolvedQueryParams = currentLocation.query;\n\n if (this.#globalCoerceSearch) {\n try {\n nfResolvedQuery = this.#globalCoerceSearch(currentLocation.query);\n } catch (err) {\n this.#reportError(err, { source: 'coerce-search' });\n }\n }\n\n const run = async (): Promise<void> => {\n if (!isCurrent()) return;\n\n const context = createRouteContext<TRoutes>(\n currentLocation,\n nfResolvedQuery,\n {},\n nfBranch,\n (target, options) => this.navigate(target, options),\n );\n\n terminalRan = await executeMiddlewarePipeline(\n context,\n [...this.#globalMiddleware, ...this.#notFoundRecord!.ownMiddleware] as unknown as Middleware<TRoutes>[],\n async () => {\n if (!isCurrent()) return;\n\n committed = true;\n commit(currentLocation, replace);\n\n if (!isCurrent()) return;\n\n await this.#runTerminal(\n this.#notFoundRecord!,\n context,\n currentLocation,\n {},\n attempt.signal,\n isCurrent,\n (error) => {\n dataError = error;\n hasDataError = true;\n },\n );\n },\n (error) =>\n isRouterErrorCarrier(error) || (hasDataError && error === dataError)\n ? error\n : carryError(error, { routeName: '__notFound__', source: 'middleware' }),\n );\n };\n\n try {\n await this.#runWithTransition(run, useTransition);\n } catch (error) {\n if (hasDataError && error === dataError) {\n throw carryError(error, { routeName: '__notFound__', source: 'data-loader' });\n }\n\n throw error;\n } finally {\n if (isCurrent() && committed) {\n this.#notifyListeners();\n this.#applyScroll(this.#currentState, previousState);\n }\n }\n\n return terminalRan && committed;\n }\n\n if (!isCurrent()) return false;\n\n commit(prepared.location, replace);\n\n if (!isCurrent()) return false;\n\n this.#currentState = createRouteState({\n location: prepared.location,\n matches: [] as RouteMatchBranch,\n status: 'idle',\n });\n this.#notifyListeners();\n this.#applyScroll(this.#currentState, previousState);\n\n return true;\n }\n\n const { branch, location, params, record, resolvedQuery } = prepared;\n let committed = false;\n let dataError: unknown;\n let hasDataError = false;\n let terminalRan = false;\n\n const run = async (): Promise<void> => {\n if (!isCurrent()) return;\n\n const context = createRouteContext<TRoutes>(location, resolvedQuery, params, branch, (target, options) =>\n this.navigate(target, options),\n );\n\n terminalRan = await executeMiddlewarePipeline(\n context,\n [...this.#globalMiddleware, ...record.ownMiddleware] as unknown as Middleware<TRoutes>[],\n async () => {\n if (!isCurrent()) return;\n\n committed = true;\n commit(location, replace);\n\n if (!isCurrent()) return;\n\n await this.#runTerminal(record, context, location, params, attempt.signal, isCurrent, (error) => {\n dataError = error;\n hasDataError = true;\n });\n },\n (error) =>\n isRouterErrorCarrier(error) || (hasDataError && error === dataError)\n ? error\n : carryError(error, { routeName: record.leaf.name, source: 'middleware' }),\n );\n };\n\n try {\n await this.#runWithTransition(run, useTransition);\n } catch (error) {\n if (hasDataError && error === dataError) {\n throw carryError(error, { routeName: record.leaf.name, source: 'data-loader' });\n }\n\n throw error;\n } finally {\n if (isCurrent() && committed) {\n this.#notifyListeners();\n this.#applyScroll(this.#currentState, previousState);\n }\n }\n\n return terminalRan && committed;\n }\n\n async #runWithTransition(run: () => Promise<void>, useTransition?: boolean): Promise<void> {\n if ((useTransition ?? this.#useViewTransition) && typeof document !== 'undefined') {\n const startViewTransition = (\n document as Document & {\n startViewTransition?: (callback: () => void | Promise<void>) => { finished: Promise<void> };\n }\n ).startViewTransition;\n\n if (startViewTransition) {\n await startViewTransition.call(document, run).finished;\n\n return;\n }\n }\n\n await run();\n }\n\n #applyScroll(to: RouteState, from: RouteState): void {\n if (!this.#scroll || typeof window === 'undefined') return;\n\n const decision = this.#scroll(to, from);\n\n if (decision === 'preserve') return;\n\n if (decision === 'top') {\n window.scrollTo(0, 0);\n\n return;\n }\n\n window.scrollTo(decision.x, decision.y);\n }\n\n // ─── Private: destination resolution ─────────────────────────────────────\n\n #resolveDestination(path: string): string {\n const parsed = new URL(path, 'http://localhost');\n const normalizedPath = stripBase(parsed.pathname, this.#base);\n\n return `${joinPaths(this.#base, normalizedPath)}${parsed.search}${parsed.hash}`;\n }\n\n #locationFromPath(path: string, historyState: unknown): RouteLocation {\n const parsed = new URL(this.#resolveDestination(path), 'http://localhost');\n\n return {\n hash: parsed.hash.replace(/^#/, ''),\n historyState,\n pathname: stripBase(parsed.pathname, this.#base),\n query: parseQuery(parsed.search),\n };\n }\n\n #hrefForLocation(location: RouteLocation): string {\n const search = new URLSearchParams();\n\n for (const [key, value] of Object.entries(location.query)) {\n if (Array.isArray(value))\n value.forEach((item) => {\n search.append(key, item);\n });\n else search.set(key, value);\n }\n\n const query = search.toString();\n const hash = location.hash ? `#${location.hash}` : '';\n\n return `${joinPaths(this.#base, location.pathname)}${query ? `?${query}` : ''}${hash}`;\n }\n\n #navigationDestination(href: string): NavigationDestination {\n const parsed = new URL(href, 'http://localhost');\n const pathname = stripBase(parsed.pathname, this.#base);\n const { params, record } = matchRouteFor(pathname, this.#records);\n\n return {\n name: record?.leaf.name,\n params,\n pathname,\n query: parseQuery(parsed.search),\n };\n }\n\n /** User-initiated navigation. History changes only after middleware reaches the terminal stage. */\n async #navigateToPath(path: string, options: NavigateOptions = {}): Promise<void> {\n this.#assertNotDisposed();\n\n const destination = this.#resolveDestination(path);\n\n if (!options.force && destination === this.#lastHref) return;\n\n const attempt = this.#navigation.begin();\n const activeMatchNames = this.#currentState.matches.map((match) => match.name);\n const allowed = await runLeaveBlockers(\n this.#beforeLeaveBlockers,\n activeMatchNames,\n this.#navigationDestination(destination),\n );\n\n if (!attempt.isCurrent() || !allowed) return;\n\n try {\n await this.#handleRoute(\n attempt,\n this.#locationFromPath(destination, options.state),\n (location, replace) => {\n if (!attempt.isCurrent()) return;\n\n const href = this.#hrefForLocation(location);\n\n if (replace) this.#history.replace(href, location.historyState);\n else this.#history.push(href, location.historyState);\n\n this.#lastHref = href;\n },\n options.viewTransition,\n 0,\n options.replace ?? false,\n );\n } catch (value) {\n throw isRouterErrorCarrier(value) ? value.error : value;\n }\n }\n}\n\n// ─── Public factory ───────────────────────────────────────────────────────────\n\n/**\n * Create a new router instance from a route table.\n *\n * @example\n * const router = createRouter({\n * routes: {\n * home: { path: '/' },\n * userDetail: { path: '/users/:id', data: fetchUser },\n * },\n * });\n */\nexport function createRouter<const TRoutes extends RouteTable>(options: RouterOptions<TRoutes>): Router<TRoutes> {\n return new Router(options);\n}\n\n// Export the Router type (not the constructor value) for type annotations.\nexport type { Router };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"wayfinder.iife.js","names":[],"sources":["../src/errors.ts","../src/path.ts","../src/history.ts","../src/middleware.ts","../src/_navigation.ts","../src/_dev.ts","../src/compile.ts","../src/context.ts","../src/guards.ts","../src/router.ts"],"sourcesContent":["/** Base class for all wayfinder errors. Use `instanceof WayfinderError` to catch any router-originated error. */\nexport class WayfinderError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when any router method is called after the router has been disposed. */\nexport class WayfinderDisposedError extends WayfinderError {\n constructor() {\n super('Router is disposed');\n }\n}\n\n/** Thrown when a route or path definition is malformed. */\nexport class WayfinderRouteError extends WayfinderError {}\n\n/** Thrown when the router detects an infinite redirect loop. */\nexport class WayfinderRedirectLoopError extends WayfinderError {\n constructor() {\n super('Redirect loop detected');\n }\n}\n\n/** Thrown on API misuse (e.g. calling next() multiple times). */\nexport class WayfinderApiError extends WayfinderError {}\n","import { WayfinderRouteError } from './errors';\nimport type { QueryParams, ResolvedQueryParams, RouteLocation, RouteMatcher, RouteParams, RouteRecord } from './types';\n\n/** Ensure leading slash, collapse duplicate slashes, preserve root. */\nexport function normalizePath(path: string): string {\n const normalized = `/${path}`.replace(/\\/+/g, '/');\n\n return normalized !== '/' && normalized.endsWith('/') ? normalized.slice(0, -1) : normalized;\n}\n\nexport function joinPaths(base: string, path: string): string {\n return normalizePath(`${base}/${path}`);\n}\n\nfunction escapeRegex(input: string): string {\n return input.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nexport function compilePathMatcher(path: string): RouteMatcher {\n const normalized = normalizePath(path);\n\n if (normalized === '/') {\n return {\n paramNames: [],\n pattern: /^\\/$/,\n prefixPattern: /^\\/$/,\n };\n }\n\n const paramNames: string[] = [];\n const segments = normalized.slice(1).split('/');\n const regexParts: string[] = ['^'];\n const prefixParts: string[] = ['^'];\n\n for (let i = 0; i < segments.length; i += 1) {\n const segment = segments[i]!;\n\n if (segment === '*') {\n if (i !== segments.length - 1) {\n throw new WayfinderRouteError(`Wildcard \"*\" must be the final segment in path: ${path}`);\n }\n\n regexParts.push('(?:/.*)?');\n break;\n }\n\n if (segment.startsWith(':') && segment.endsWith('*')) {\n if (i !== segments.length - 1) {\n throw new WayfinderRouteError(`Wildcard param must be final segment in path: ${path}`);\n }\n\n const name = segment.slice(1, -1);\n\n if (!/^\\w+$/.test(name)) {\n throw new WayfinderRouteError(\n `Invalid param name \":${name}\" in path \"${path}\". Param names must only contain word characters (a–z, A–Z, 0–9, _).`,\n );\n }\n\n paramNames.push(name);\n regexParts.push(i === segments.length - 1 ? '(?:/(.*))?' : '/(.*)');\n prefixParts.push('/.*');\n break;\n }\n\n if (segment.startsWith(':')) {\n const name = segment.slice(1);\n\n if (!/^\\w+$/.test(name)) {\n throw new WayfinderRouteError(\n `Invalid param name \":${name}\" in path \"${path}\". Param names must only contain word characters (a–z, A–Z, 0–9, _).`,\n );\n }\n\n paramNames.push(name);\n regexParts.push('/([^/]+)');\n prefixParts.push('/[^/]+');\n continue;\n }\n\n const escaped = escapeRegex(segment);\n\n regexParts.push(`/${escaped}`);\n prefixParts.push(`/${escaped}`);\n }\n\n regexParts.push('$');\n prefixParts.push('(?:/.*)?$');\n\n return {\n paramNames,\n pattern: new RegExp(regexParts.join('')),\n prefixPattern: new RegExp(prefixParts.join('')),\n };\n}\n\nexport function matchRecord(pathname: string, record: RouteRecord): RouteParams | null {\n const match = record.matcher.pattern.exec(pathname);\n\n if (!match) return null;\n\n const params: RouteParams = {};\n\n record.matcher.paramNames.forEach((name: string, index: number) => {\n const value = match[index + 1] ?? '';\n\n try {\n params[name] = decodeURIComponent(value);\n } catch {\n params[name] = value;\n }\n });\n\n return params;\n}\n\nexport function matchRouteFor(\n pathname: string,\n records: readonly RouteRecord[],\n): { params: RouteParams; record?: RouteRecord } {\n for (const record of records) {\n const params = matchRecord(pathname, record);\n\n if (params) return { params, record };\n }\n\n return { params: {} };\n}\n\nexport function matchesPrefix(pathname: string, record: RouteRecord): boolean {\n return record.matcher.prefixPattern.test(pathname);\n}\n\n/**\n * Parse `?foo=a&foo=b&bar=c` into `{ foo: ['a', 'b'], bar: 'c' }`.\n *\n * @security Query keys come directly from the (attacker-controlled) URL. Plain bracket\n * assignment (`out[key] = value`) would let a key like `__proto__` reach the inherited\n * `Object.prototype.__proto__` setter and silently rewrite `out`'s own prototype instead of\n * storing a value — `setQueryValue` uses `Object.defineProperty` so every key, including\n * `__proto__`/`constructor`/`prototype`, is always stored as a plain own data property.\n */\nexport function parseQuery(queryString: string): QueryParams {\n const search = new URLSearchParams(queryString);\n const out: QueryParams = {};\n\n const setQueryValue = (key: string, value: QueryParams[string]): void => {\n Object.defineProperty(out, key, { configurable: true, enumerable: true, value, writable: true });\n };\n\n for (const [key, value] of search.entries()) {\n const existing = Object.hasOwn(out, key) ? out[key] : undefined;\n\n if (existing === undefined) {\n setQueryValue(key, value);\n continue;\n }\n\n if (Array.isArray(existing)) {\n existing.push(value);\n continue;\n }\n\n setQueryValue(key, [existing, value]);\n }\n\n return out;\n}\n\n/** Build a URL from a path pattern, params, and query, respecting the router base. */\nexport function buildUrl(base: string, pattern: string, params: RouteParams = {}, query?: ResolvedQueryParams): string {\n let path = pattern.replace(/:(\\w+)(\\*)?/g, (_match, key: string, isWildcard: string) => {\n const value = params[key];\n\n if (value == null) throw new WayfinderRouteError(`Missing path param: ${key}`);\n\n if (isWildcard) {\n return value\n .split('/')\n .map((segment) => encodeURIComponent(segment))\n .join('/');\n }\n\n return encodeURIComponent(value);\n });\n\n if (query && Object.keys(query).length) {\n const search = new URLSearchParams();\n\n Object.entries(query).forEach(([key, value]) => {\n if (Array.isArray(value))\n value.forEach((item) => {\n search.append(key, String(item));\n });\n else search.set(key, String(value));\n });\n\n path += `?${search.toString()}`;\n }\n\n return joinPaths(base, path);\n}\n\n// ─── Location helpers (formerly in resolve.ts) ────────────────────────────────\n\nexport function stripBase(pathname: string, base = '/'): string {\n const normalizedPath = normalizePath(pathname);\n const normalizedBase = normalizePath(base);\n\n if (normalizedBase === '/') return normalizedPath;\n\n if (normalizedPath === normalizedBase) return '/';\n\n const prefix = `${normalizedBase}/`;\n\n return normalizedPath.startsWith(prefix)\n ? normalizePath(normalizedPath.slice(normalizedBase.length))\n : normalizedPath;\n}\n\nexport function readLocation(\n base: string,\n history: { location: { hash: string; pathname: string; search: string; state: unknown } },\n): RouteLocation {\n return {\n hash: history.location.hash.replace(/^#/, ''),\n historyState: history.location.state,\n pathname: stripBase(history.location.pathname || '/', base),\n query: parseQuery(history.location.search || ''),\n };\n}\n","import { joinPaths, normalizePath, stripBase } from './path';\nimport type { HistoryDriver } from './types';\n\n/** Creates a history driver backed by the browser History API. */\nexport function createBrowserHistory(): HistoryDriver {\n return {\n back() {\n window.history.back();\n },\n get location() {\n return {\n hash: window.location.hash,\n pathname: window.location.pathname,\n search: window.location.search,\n // State lives on window.history.state, not window.location.\n state: window.history.state,\n };\n },\n onPopstate(listener) {\n window.addEventListener('popstate', listener);\n\n return () => window.removeEventListener('popstate', listener);\n },\n push(url, state) {\n window.history.pushState(state, '', url);\n },\n replace(url, state) {\n window.history.replaceState(state, '', url);\n },\n };\n}\n\nexport type HashHistoryOptions = { base?: string };\n\nexport function createHashHistory(options: HashHistoryOptions = {}): HistoryDriver {\n const base = normalizePath(options.base ?? '/');\n const baseRoot = base === '/' ? '/' : `${base}/`;\n const location = () => {\n const parsed = new URL(window.location.hash.slice(1) || '/', 'http://localhost');\n\n return {\n hash: parsed.hash,\n pathname: joinPaths(base, parsed.pathname),\n search: parsed.search,\n state: window.history.state,\n };\n };\n const destination = (url: string) => {\n const parsed = new URL(url, 'http://localhost');\n const pathname = stripBase(parsed.pathname, base);\n\n return `${baseRoot}#${pathname}${parsed.search}${parsed.hash}`;\n };\n\n return {\n back() {\n window.history.back();\n },\n get location() {\n return location();\n },\n onPopstate(listener) {\n window.addEventListener('popstate', listener);\n\n return () => window.removeEventListener('popstate', listener);\n },\n push(url, state) {\n window.history.pushState(state, '', destination(url));\n },\n replace(url, state) {\n window.history.replaceState(state, '', destination(url));\n },\n };\n}\n\ntype MemoryLocation = { hash: string; pathname: string; search: string; state: unknown };\n\n/**\n * Creates an in-memory history driver. Suitable for SSR, tests, and non-browser environments.\n *\n * Semantics mirror the browser History API:\n * - `push()` and `replace()` update the location silently (no listener notification),\n * matching `pushState`/`replaceState` which do not fire `popstate`.\n * - `back()` moves one entry back and notifies subscribers, matching a browser back-button press.\n */\nexport function createMemoryHistory(initialPath = '/'): HistoryDriver {\n const parsed = new URL(initialPath, 'http://localhost');\n const stack: MemoryLocation[] = [\n { hash: parsed.hash, pathname: parsed.pathname, search: parsed.search, state: null },\n ];\n let cursor = 0;\n const listeners = new Set<() => void>();\n\n return {\n back() {\n if (cursor <= 0) return;\n\n cursor -= 1;\n listeners.forEach((l) => {\n l();\n });\n },\n get location(): MemoryLocation {\n return stack[cursor]!;\n },\n onPopstate(listener) {\n listeners.add(listener);\n\n return () => listeners.delete(listener);\n },\n push(url, state = null) {\n const p = new URL(url, 'http://localhost');\n\n stack.splice(cursor + 1);\n stack.push({ hash: p.hash, pathname: p.pathname, search: p.search, state });\n cursor = stack.length - 1;\n // Silent — mirrors browser pushState semantics. Router drives navigation via #handleRoute directly.\n },\n replace(url, state = null) {\n const p = new URL(url, 'http://localhost');\n\n stack[cursor] = { hash: p.hash, pathname: p.pathname, search: p.search, state };\n // Silent — mirrors browser replaceState semantics.\n },\n };\n}\n","import type { Middleware, NamedNavigationTarget, NavigateOptions, RawNavigationTarget, RouteTable } from './types';\n\n/**\n * Middleware helper that redirects navigation to another route.\n * Attach to a route's `middleware` array for declarative redirect patterns.\n *\n * Note: `next()` is intentionally not called — this middleware blocks the chain\n * and immediately navigates to the target.\n *\n * @example\n * // Redirect /old → /new\n * routes: {\n * old: { middleware: [redirectTo({ path: '/new' })], path: '/old' },\n * new: { path: '/new' },\n * }\n */\nexport function redirectTo<TRoutes extends RouteTable = RouteTable>(\n target: NamedNavigationTarget<TRoutes> | RawNavigationTarget | string,\n options?: NavigateOptions,\n): Middleware<TRoutes> {\n return async (context) => {\n await context.navigate(target, options);\n };\n}\n","export type NavigationAttempt = {\n isCurrent(): boolean;\n readonly signal: AbortSignal;\n};\n\n/** Coordinates navigation ownership so stale work cannot commit router state. */\nexport function createNavigationCoordinator() {\n let active: AbortController | undefined;\n\n return {\n begin(): NavigationAttempt {\n active?.abort();\n\n const controller = new AbortController();\n\n active = controller;\n\n return {\n isCurrent: () => active === controller && !controller.signal.aborted,\n signal: controller.signal,\n };\n },\n\n invalidate(reason?: unknown): void {\n active?.abort(reason);\n active = undefined;\n },\n };\n}\n","const isDev = !(globalThis as { __WAYFINDER_PROD__?: boolean }).__WAYFINDER_PROD__;\n\n/** @internal @security Messages may include user-supplied data. */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/wayfinder] ${msg}`);\n}\n\n/** @internal */\nexport function error(msg: string, ...args: unknown[]): void {\n if (isDev) console.error(`[@vielzeug/wayfinder] ${msg}`, ...args);\n}\n","import { warn } from './_dev';\nimport { WayfinderRouteError } from './errors';\nimport { compilePathMatcher, joinPaths, normalizePath } from './path';\nimport type { Middleware, RouteBranchDef, RouteDefinition, RouteRecord, RouterOptions, RouteTable } from './types';\n\nexport type CompiledRoutes = {\n records: readonly RouteRecord[];\n routesByName: ReadonlyMap<string, RouteRecord>;\n};\n\nexport function compileRoutes<TRoutes extends RouteTable>(options: RouterOptions<TRoutes>): CompiledRoutes {\n const records: RouteRecord[] = [];\n\n const compile = (\n name: string,\n route: RouteDefinition,\n ancestorPath: string,\n ancestorBranchDefs: RouteBranchDef[],\n ancestorMiddleware: RouteRecord['ownMiddleware'],\n ): void => {\n if (route.index && route.path !== undefined) {\n throw new WayfinderRouteError(`Route \"${name}\" cannot define both index and path`);\n }\n\n if (!route.index && route.path === undefined) {\n throw new WayfinderRouteError(`Route \"${name}\" must define path or set index: true`);\n }\n\n const ownPath = route.index\n ? ancestorPath\n : normalizePath(route.path ? joinPaths(ancestorPath, route.path) : ancestorPath);\n\n const branchDefs: RouteBranchDef[] = [\n ...ancestorBranchDefs,\n {\n dataFn: route.data,\n name,\n onError: route.onError as RouteBranchDef['onError'],\n },\n ];\n\n const ownMiddleware: Middleware[] = [\n ...ancestorMiddleware,\n ...((route.middleware ?? []) as unknown as Middleware[]),\n ];\n\n if (route.children) {\n for (const [childName, childRoute] of Object.entries(route.children)) {\n compile(`${name}.${childName}`, childRoute, ownPath, branchDefs, ownMiddleware);\n }\n }\n\n // Emit a record for leaf routes (no children) and routes with data or redirect.\n // A bare parent-only route (children but no data/redirect) is not emitted as a leaf.\n if (route.redirect !== undefined || !route.children || route.data !== undefined) {\n const leaf = branchDefs[branchDefs.length - 1]!;\n\n records.push({\n branchDefs,\n coerceSearch: route.coerceSearch,\n leaf,\n matcher: compilePathMatcher(ownPath),\n ownMiddleware,\n path: ownPath,\n redirect: route.redirect,\n });\n }\n };\n\n for (const [name, route] of Object.entries(options.routes)) {\n compile(name, route, '/', [], []);\n }\n\n // Validate duplicate names.\n const namesSeen = new Set<string>();\n\n for (const record of records) {\n if (namesSeen.has(record.leaf.name)) {\n throw new WayfinderRouteError(\n `Duplicate route name: \"${record.leaf.name}\". A top-level route key must not coincide with a nested route's dot-notation name.`,\n );\n }\n\n namesSeen.add(record.leaf.name);\n }\n\n // Warn when a catch-all or wildcard-param route precedes more specific routes.\n // Routes match in array order (object key order), so a wildcard placed too early silently shadows later routes.\n let wildcardSeen = false;\n\n for (const record of records) {\n const isCatchAll =\n record.path === '/*' || record.path.endsWith('/*') || (record.path.includes(':') && record.path.endsWith('*'));\n\n if (wildcardSeen && !isCatchAll) {\n warn(\n `Route \"${record.leaf.name}\" (${record.path}) is defined after a wildcard/catch-all route and will never match. Move specific routes before wildcards.`,\n );\n }\n\n if (isCatchAll) wildcardSeen = true;\n }\n\n return {\n records,\n routesByName: new Map(records.map((r) => [r.leaf.name, r])),\n };\n}\n","import { error as logError } from './_dev';\nimport { WayfinderApiError } from './errors';\nimport type {\n Middleware,\n NavigationStatus,\n ResolvedQueryParams,\n RouteBranchDef,\n RouteContext,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteParams,\n RouterErrorContext,\n RouteState,\n RouteTable,\n} from './types';\n\n// ─── Route state ──────────────────────────────────────────────────────────────\n\n/** No defensive deep-copy — RouteState is typed readonly; callers always pass fresh objects. */\nexport function createRouteState(input: {\n error?: unknown;\n location: RouteLocation;\n matches: RouteMatchBranch;\n status: NavigationStatus;\n}): RouteState {\n return {\n error: input.error,\n location: input.location,\n matches: input.matches,\n status: input.status,\n };\n}\n\n/**\n * Build a RouteMatchBranch from compiled defs and data results.\n */\nexport function buildMatchBranch(\n branchDefs: readonly RouteBranchDef[],\n params: RouteParams,\n pathname: string,\n dataResults: unknown[],\n): RouteMatchBranch {\n return branchDefs.map(\n (def, i): RouteMatch => ({\n data: dataResults[i],\n name: def.name,\n params: { ...params },\n pathname,\n }),\n );\n}\n\n// ─── Context factory ──────────────────────────────────────────────────────────\n\nexport function createRouteContext<TRoutes extends RouteTable>(\n location: RouteLocation,\n resolvedQuery: ResolvedQueryParams,\n params: RouteParams,\n matches: RouteMatchBranch,\n navigate: RouteContext<RouteParams, TRoutes>['navigate'],\n): RouteContext<RouteParams, TRoutes> {\n return {\n hash: location.hash,\n historyState: location.historyState,\n locals: {},\n matches,\n navigate,\n params,\n pathname: location.pathname,\n query: resolvedQuery,\n };\n}\n\n// ─── Middleware pipeline ──────────────────────────────────────────────────────\n\nexport async function executeMiddlewarePipeline<TRoutes extends RouteTable>(\n context: RouteContext<RouteParams, TRoutes>,\n middleware: readonly Middleware<TRoutes>[],\n terminal: () => Promise<void>,\n wrapMiddlewareError?: (error: unknown) => unknown,\n): Promise<boolean> {\n let terminalRan = false;\n\n async function dispatch(index: number): Promise<void> {\n if (index < middleware.length) {\n let called = false;\n\n try {\n await middleware[index]?.(context, async () => {\n if (called) throw new WayfinderApiError('next() called multiple times');\n\n called = true;\n await dispatch(index + 1);\n });\n } catch (error) {\n throw wrapMiddlewareError?.(error) ?? error;\n }\n\n return;\n }\n\n terminalRan = true;\n await terminal();\n }\n\n await dispatch(0);\n\n return terminalRan;\n}\n\n// ─── Error reporting ──────────────────────────────────────────────────────────\n\nexport function reportError(\n error: unknown,\n context: RouterErrorContext,\n onError?: (e: unknown, ctx: RouterErrorContext) => void,\n): void {\n if (onError) {\n onError(error, context);\n\n return;\n }\n\n logError('Unhandled router error. Provide an onError callback to handle errors explicitly:', error);\n}\n","import type { BeforeLeaveBlocker, NavigationDestination } from './types';\n\n/**\n * A registered leave guard. `routes` scopes the guard to navigations that depart from\n * any route whose name appears in the array (matched against any node in the active branch,\n * not just the leaf). When `routes` is omitted the guard fires on every navigation.\n */\nexport type RegisteredBlocker = {\n handler: BeforeLeaveBlocker;\n routes?: string[];\n};\n\n/**\n * Run all registered leave blockers in registration order.\n * Guards whose `routes` filter does not overlap the current active match names are skipped.\n * Returns `false` as soon as any blocker returns `false`; returns `true` if all pass.\n *\n * A snapshot of the blocker set is taken before iteration so that guards added or removed\n * during execution do not affect the current run.\n *\n * `destination` is passed to each blocker so guards can make context-aware decisions.\n */\nexport async function runLeaveBlockers(\n blockers: ReadonlySet<RegisteredBlocker>,\n activeMatchNames: readonly string[],\n destination: NavigationDestination,\n): Promise<boolean> {\n const activeNames = new Set(activeMatchNames);\n // Snapshot so that removals during iteration don't skip entries.\n const snapshot = [...blockers];\n\n for (const { handler, routes } of snapshot) {\n if (routes !== undefined && !routes.some((name) => activeNames.has(name))) continue;\n\n const allowed = await handler(destination);\n\n if (!allowed) return false;\n }\n\n return true;\n}\n","import { createNavigationCoordinator, type NavigationAttempt } from './_navigation';\nimport { compileRoutes } from './compile';\nimport {\n buildMatchBranch,\n createRouteContext,\n createRouteState,\n executeMiddlewarePipeline,\n reportError,\n} from './context';\nimport { WayfinderDisposedError, WayfinderRedirectLoopError, WayfinderRouteError } from './errors';\nimport { type RegisteredBlocker, runLeaveBlockers } from './guards';\nimport { createBrowserHistory } from './history';\nimport {\n buildUrl,\n joinPaths,\n matchesPrefix,\n matchRouteFor,\n normalizePath,\n parseQuery,\n readLocation,\n stripBase,\n} from './path';\nimport type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n HistoryDriver,\n IsActiveOptions,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n PathParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n RouteBranchDef,\n RouteContext,\n RouteLocation,\n RouteMatchBranch,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouteRecord,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n RouteViewMap,\n RouteViewName,\n RouteViewRegistry,\n Unsubscribe,\n} from './types';\n\n// ─── Module-level helpers ────────────────────────────────────────────────────\n\nfunction getRouteByName(name: string, routesByName: ReadonlyMap<string, RouteRecord>): RouteRecord {\n const route = routesByName.get(name);\n\n if (route) return route;\n\n const available = [...routesByName.keys()].join(', ');\n\n throw new WayfinderRouteError(\n available ? `Unknown route name: ${name}. Available routes: ${available}` : `Unknown route name: ${name}`,\n );\n}\n\nfunction resolveTarget(\n target: { path: string } | { hash?: string; name: string; params?: RouteParams; query?: ResolvedQueryParams },\n routesByName: ReadonlyMap<string, RouteRecord>,\n base = '/',\n): string {\n if ('path' in target) return target.path;\n\n const route = getRouteByName(target.name, routesByName);\n const path = buildUrl(base, route.path, target.params, target.query);\n\n return target.hash ? `${path}#${target.hash}` : path;\n}\n\n// ─── Internal error carrier ──────────────────────────────────────────────────\n//\n// Carries error context alongside the original error without mutating the thrown\n// object. The carrier is unwrapped before reaching external callers so error\n// identity and `cause` chains are preserved.\n\nconst ROUTER_ERROR = Symbol('wayfinder.routerError');\n\ntype RouterErrorCarrier = {\n [ROUTER_ERROR]: true;\n readonly error: unknown;\n readonly context: RouterErrorContext;\n};\n\nfunction carryError(error: unknown, context: RouterErrorContext): RouterErrorCarrier {\n return { [ROUTER_ERROR]: true, context, error };\n}\n\nfunction isRouterErrorCarrier(value: unknown): value is RouterErrorCarrier {\n return value !== null && typeof value === 'object' && ROUTER_ERROR in (value as Record<symbol, unknown>);\n}\n\nfunction unwrapCarrier(value: unknown): never {\n if (isRouterErrorCarrier(value)) throw value.error;\n\n throw value;\n}\n\n// ─── Internal types ───────────────────────────────────────────────────────────\n\ntype NavigationDecision =\n | {\n branch: RouteMatchBranch;\n location: RouteLocation;\n params: RouteParams;\n record: RouteRecord;\n resolvedQuery: ResolvedQueryParams;\n type: 'matched';\n }\n | { location: RouteLocation; params: RouteParams; type: 'unmatched' }\n | { location: RouteLocation; params: RouteParams; redirectTo: string; type: 'redirect' };\n\n// ─── Router class ─────────────────────────────────────────────────────────────\n\nclass Router<TRoutes extends RouteTable> {\n readonly #base: string;\n readonly #globalMiddleware: readonly Middleware[];\n readonly #globalCoerceSearch?: CoerceSearchFn;\n readonly #history: HistoryDriver;\n readonly #records: readonly RouteRecord[];\n readonly #routesByName: ReadonlyMap<string, RouteRecord>;\n readonly #onError?: RouterOptions<TRoutes>['onError'];\n readonly #scroll?: RouterOptions<TRoutes>['scroll'];\n readonly #useViewTransition: boolean;\n\n // Mutable navigation state\n readonly #beforeLeaveBlockers = new Set<RegisteredBlocker>();\n readonly #navigation = createNavigationCoordinator();\n #currentState: RouteState;\n #disposed = false;\n readonly #disposeController = new AbortController();\n #lastHref = '/';\n readonly #listeners = new Set<(state: RouteState) => void>();\n readonly #preloaded = new Map<string, unknown[]>();\n readonly #preloads = new Map<string, Promise<RouteState | null>>();\n // Compiled notFound fallback record\n readonly #notFoundRecord: RouteRecord | null;\n\n readonly #unlistenHistory: () => void;\n\n /** Resolves when the constructor-triggered navigation has settled; rejects if it fails. */\n readonly ready: Promise<void>;\n\n constructor(options: RouterOptions<TRoutes>) {\n const compiled = compileRoutes(options);\n\n this.#base = normalizePath(options.base ?? '/');\n this.#globalMiddleware = (options.middleware ?? []) as unknown as Middleware[];\n this.#globalCoerceSearch = options.coerceSearch;\n this.#history = options.history ?? createBrowserHistory();\n this.#onError = options.onError;\n this.#scroll = options.scroll;\n this.#useViewTransition = options.viewTransition ?? false;\n this.#records = compiled.records as unknown as readonly RouteRecord[];\n this.#routesByName = compiled.routesByName as unknown as ReadonlyMap<string, RouteRecord>;\n this.#currentState = createRouteState({\n location: { hash: '', historyState: null, pathname: '/', query: {} },\n matches: [] as RouteMatchBranch,\n status: 'idle',\n });\n\n // Build a synthetic RouteRecord for the notFound fallback.\n if (options.notFound) {\n const nf = options.notFound;\n const leafDef: RouteBranchDef = {\n dataFn: nf.data,\n name: '__notFound__',\n };\n\n this.#notFoundRecord = {\n branchDefs: [leafDef],\n leaf: leafDef,\n matcher: { paramNames: [], pattern: /(?:)/, prefixPattern: /(?:)/ },\n ownMiddleware: (nf.middleware ?? []) as unknown as Middleware[],\n path: '/*',\n };\n } else {\n this.#notFoundRecord = null;\n }\n\n this.#unlistenHistory = this.#registerHistoryListener();\n\n const { hash, pathname, search } = this.#history.location;\n\n this.#lastHref = `${pathname}${search}${hash}`;\n\n const attempt = this.#navigation.begin();\n\n const navigationPromise = this.#handleRoute(\n attempt,\n readLocation(this.#base, this.#history),\n (location, replace) => {\n if (!attempt.isCurrent()) return;\n\n const href = this.#hrefForLocation(location);\n\n if (replace) this.#history.replace(href, location.historyState);\n\n this.#lastHref = href;\n },\n undefined,\n 0,\n false,\n ).then(() => undefined);\n\n // ready rejects with the original error (carrier unwrapped) for external consumers.\n this.ready = navigationPromise.catch(unwrapCarrier);\n // Attach a silent handler so an unawaited ready promise doesn't trigger\n // an unhandled-rejection warning. External consumers can still await ready\n // and receive the rejection — each .catch() subscriber fires independently.\n this.ready.catch(() => {});\n // #runInBackground receives the raw promise so it can extract carrier context for onError.\n this.#runInBackground(navigationPromise, { source: 'initial-navigation' });\n\n // Router actions are intentionally bound once so they remain safe when destructured.\n this.beforeLeave = this.beforeLeave.bind(this);\n this.createViewRegistry = this.createViewRegistry.bind(this);\n this.dispose = this.dispose.bind(this);\n this.getSnapshot = this.getSnapshot.bind(this);\n this.isActive = this.isActive.bind(this);\n this.load = this.load.bind(this);\n this.match = this.match.bind(this);\n this.navigate = this.navigate.bind(this);\n this.preload = this.preload.bind(this);\n this.subscribe = this.subscribe.bind(this);\n this.url = this.url.bind(this);\n this.waitFor = this.waitFor.bind(this);\n }\n\n // ─── Public state ─────────────────────────────────────────────────────────\n\n /**\n * Returns the current immutable router state snapshot.\n *\n * Compatible with React's `useSyncExternalStore`:\n * ```ts\n * const state = useSyncExternalStore(\n * (cb) => router.subscribe(cb),\n * () => router.getSnapshot()\n * );\n * ```\n */\n getSnapshot(): RouteState {\n return this.#currentState;\n }\n\n createViewRegistry<const TViews extends RouteViewMap<TRoutes>, TNotFound = never>(\n views: TViews & Record<Exclude<keyof TViews, RouteViewName<TRoutes>>, never>,\n options?: { notFound?: TNotFound },\n ): RouteViewRegistry<TViews[keyof TViews] | TNotFound> {\n return {\n resolve: (state) => {\n const name = state.matches.at(-1)?.name;\n\n if (name === '__notFound__') return options?.notFound;\n\n return name ? (views[name as keyof TViews] as TViews[keyof TViews]) : undefined;\n },\n };\n }\n\n // ─── Subscription ─────────────────────────────────────────────────────────\n\n /**\n * Subscribe to state changes. The listener is called on each subsequent state change;\n * use `getSnapshot()` to read the current value immediately.\n */\n subscribe(listener: (state: RouteState) => void): Unsubscribe {\n this.#assertNotDisposed();\n this.#listeners.add(listener);\n\n return () => {\n this.#listeners.delete(listener);\n };\n }\n\n // ─── Navigation ───────────────────────────────────────────────────────────\n\n /**\n * Navigate using a named route target or a raw path target.\n *\n * If a data loader throws after terminal execution begins, history has already committed\n * the destination and the returned Promise rejects with the loader error.\n */\n navigate(\n target: NamedNavigationTarget<TRoutes> | RawNavigationTarget | string,\n options?: NavigateOptions,\n ): Promise<void> {\n const normalized = typeof target === 'string' ? { path: target } : target;\n const destination = resolveTarget(normalized, this.#routesByName);\n\n return this.#navigateToPath(destination, options);\n }\n\n // ─── URL helpers ──────────────────────────────────────────────────────────\n\n /** Build a URL for a named route, including optional params and query string. */\n url<Name extends RouteName<TRoutes>>(\n name: Name,\n params?: PathParams<RoutePathByName<TRoutes, Name>>,\n query?: ResolvedQueryParams,\n ): string {\n const route = getRouteByName(name, this.#routesByName);\n\n return buildUrl(this.#base, route.path, params, query);\n }\n\n /** Returns true when the current location matches the named route by prefix (default) or exactly. */\n isActive<Name extends RouteName<TRoutes>>(name: Name, options: IsActiveOptions = {}): boolean {\n const route = getRouteByName(name, this.#routesByName);\n const pathname = this.#currentState.location.pathname;\n const exact = options.exact ?? false;\n\n return exact ? matchRouteFor(pathname, [route]).record != null : matchesPrefix(pathname, route);\n }\n\n /** Match a pathname to a route branch without running middleware or data loaders. Returns null for redirects or no match. */\n match(pathname: string): RouteMatchBranch | null {\n const normalizedPathname = stripBase(normalizePath(pathname), this.#base);\n const { params, record } = matchRouteFor(normalizedPathname, this.#records);\n\n if (!record || record.redirect) return null;\n\n const branch = buildMatchBranch(\n record.branchDefs,\n params,\n normalizedPathname,\n record.branchDefs.map(() => undefined),\n );\n\n return branch.length ? branch : null;\n }\n\n /**\n * Load a URL into a route state including data loader results, without modifying\n * router state or history. Follows declarative redirects but does not cache results.\n * Middleware is not executed — use `navigate()` when middleware side effects are needed.\n */\n async load(url: string, options?: { signal?: AbortSignal }): Promise<RouteState | null> {\n const prepared = await this.#resolveUrl(url);\n\n if (prepared.type !== 'matched') return null;\n\n const { location, params, record, resolvedQuery } = prepared;\n const defs = record.branchDefs;\n const hasData = defs.some((d) => d.dataFn != null);\n let dataResults: unknown[] = defs.map(() => undefined);\n let error: unknown;\n let status: 'error' | 'idle' = 'idle';\n\n if (hasData) {\n const effectiveSignal = options?.signal ?? new AbortController().signal;\n const branch = buildMatchBranch(defs, params, location.pathname, dataResults);\n const context = createRouteContext<TRoutes>(location, resolvedQuery, params, branch, () => Promise.resolve());\n\n try {\n dataResults = await this.#loadData(defs, context, effectiveSignal);\n } catch (e) {\n error = e;\n status = 'error';\n }\n }\n\n return createRouteState({\n error,\n location,\n matches: buildMatchBranch(defs, params, location.pathname, dataResults),\n status,\n });\n }\n\n async preload(target: NamedNavigationTarget<TRoutes>): Promise<RouteState | null> {\n this.#assertNotDisposed();\n\n const destination = this.#resolveDestination(resolveTarget(target, this.#routesByName, this.#base));\n const existing = this.#preloads.get(destination);\n\n if (existing) return existing;\n\n const work = this.load(destination, { signal: this.#disposeController.signal })\n .then((state) => {\n if (this.#disposed) throw this.#disposeController.signal.reason;\n\n if (state?.status === 'error') throw state.error;\n\n if (state) {\n const key = this.#hrefForLocation(state.location);\n\n if (this.#preloaded.size >= 20) this.#preloaded.delete(this.#preloaded.keys().next().value!);\n\n this.#preloaded.set(\n key,\n state.matches.map((match) => match.data),\n );\n }\n\n return state;\n })\n .catch((error) => {\n if (this.#onError) this.#reportError(error, { source: 'preload' });\n\n throw error;\n })\n .finally(() => {\n this.#preloads.delete(destination);\n });\n\n this.#preloads.set(destination, work);\n\n return work;\n }\n\n /**\n * Returns a Promise that resolves the next time the router reaches `status: 'idle'`\n * and the active matches include a route named `name`.\n *\n * - Resolves immediately if the router is already `idle` at `name`.\n * - Rejects immediately if the router is already in `status: 'error'`.\n * - If the router is currently `idle` at a **different** route and no navigation is in\n * flight, this promise will not resolve until a future navigation lands on `name`.\n * Typical use-case is awaiting a navigation you just triggered:\n * `router.navigate(target); await router.waitFor('routeName')`.\n * @throws {WayfinderDisposedError} if the router is disposed while the promise is pending,\n * or if called after the router has already been disposed.\n */\n waitFor(name: RouteName<TRoutes>): Promise<RouteState> {\n this.#assertNotDisposed();\n\n return new Promise((resolve, reject) => {\n const matchesName = (state: RouteState): boolean =>\n state.status === 'idle' && state.matches.some((m) => m.name === name);\n\n if (this.#currentState.status === 'error') {\n reject(this.#currentState.error);\n\n return;\n }\n\n if (matchesName(this.#currentState)) {\n resolve(this.#currentState);\n\n return;\n }\n\n const unsub = this.subscribe((state) => {\n if (matchesName(state)) {\n unsub();\n resolve(state);\n } else if (state.status === 'error') {\n unsub();\n reject(state.error);\n }\n });\n\n this.#disposeController.signal.addEventListener(\n 'abort',\n () => {\n unsub();\n reject(this.#disposeController.signal.reason);\n },\n { once: true },\n );\n });\n }\n\n // ─── Navigation guards ────────────────────────────────────────────────────\n\n /**\n * Register a leave guard. Called before user-triggered navigation attempts.\n * Return `false` to cancel; `true` to allow.\n * Returns a function that removes the guard.\n *\n * Use `options.routes` to scope the guard to specific routes (fires only when navigating\n * away from a route whose name appears in the array, checked against any node in the active branch).\n */\n beforeLeave(blocker: BeforeLeaveBlocker, options?: BeforeLeaveOptions<TRoutes>): Unsubscribe {\n this.#assertNotDisposed();\n\n const entry: RegisteredBlocker = {\n handler: blocker,\n routes: options?.routes as string[] | undefined,\n };\n\n this.#beforeLeaveBlockers.add(entry);\n\n return () => {\n this.#beforeLeaveBlockers.delete(entry);\n };\n }\n\n // ─── Lifecycle ────────────────────────────────────────────────────────────\n\n get disposalSignal(): AbortSignal {\n return this.#disposeController.signal;\n }\n\n get disposed(): boolean {\n return this.#disposed;\n }\n\n /** Dispose event listeners and prevent further router interaction. Idempotent. */\n dispose(): void {\n if (this.#disposed) return;\n\n this.#disposed = true;\n this.#beforeLeaveBlockers.clear();\n this.#listeners.clear();\n this.#preloaded.clear();\n this.#preloads.clear();\n this.#navigation.invalidate(new WayfinderDisposedError());\n this.#unlistenHistory();\n // Abort the disposal signal last — waitFor() listeners clean themselves up via this signal.\n this.#disposeController.abort(new WayfinderDisposedError());\n }\n\n [Symbol.dispose](): void {\n this.dispose();\n }\n\n // ─── Private: assertions ──────────────────────────────────────────────────\n\n #assertNotDisposed(): void {\n if (this.#disposed) throw new WayfinderDisposedError();\n }\n\n // ─── Private: history listener ────────────────────────────────────────────\n\n #registerHistoryListener(): () => void {\n return this.#history.onPopstate(() => {\n const { hash, pathname, search } = this.#history.location;\n const newHref = `${pathname}${search}${hash}`;\n const previousHref = this.#lastHref;\n\n if (newHref === previousHref) return;\n\n this.#runInBackground(this.#handleHistoryNavigation(newHref, previousHref), { source: 'history-listener' });\n });\n }\n\n async #handleHistoryNavigation(newHref: string, previousHref: string): Promise<void> {\n const attempt = this.#navigation.begin();\n const activeMatchNames = this.#currentState.matches.map((m) => m.name);\n const destination = this.#navigationDestination(newHref);\n const allowed = await runLeaveBlockers(this.#beforeLeaveBlockers, activeMatchNames, destination);\n\n if (!attempt.isCurrent()) return;\n\n if (!allowed) {\n this.#history.replace(previousHref, this.#currentState.location.historyState);\n\n return;\n }\n\n const terminalRan = await this.#handleRoute(\n attempt,\n readLocation(this.#base, this.#history),\n (location, replace) => {\n if (!attempt.isCurrent()) return;\n\n const href = this.#hrefForLocation(location);\n\n if (replace) this.#history.replace(href, location.historyState);\n\n this.#lastHref = href;\n },\n undefined,\n 0,\n false,\n );\n\n if (attempt.isCurrent() && !terminalRan) {\n this.#history.replace(previousHref, this.#currentState.location.historyState);\n }\n }\n\n // ─── Private: error handling ──────────────────────────────────────────────\n\n #reportError(error: unknown, context: RouterErrorContext): void {\n reportError(error, context, this.#onError);\n }\n\n #runInBackground(promise: Promise<void>, context: RouterErrorContext): void {\n void promise.catch((value) => {\n if (isRouterErrorCarrier(value)) {\n this.#reportError(value.error, value.context);\n } else {\n this.#reportError(value, context);\n }\n });\n }\n\n // ─── Private: listener notification ──────────────────────────────────────\n\n #notifyListeners(): void {\n this.#listeners.forEach((listener) => {\n listener(this.#currentState);\n });\n }\n\n // ─── Private: data loaders ────────────────────────────────────────────────\n\n /**\n * Run all data loaders and return their results. Per-def `onError` boundaries are applied.\n */\n async #loadData(\n defs: readonly RouteBranchDef[],\n context: RouteContext<RouteParams, TRoutes>,\n signal: AbortSignal,\n ): Promise<unknown[]> {\n return Promise.all(\n defs.map(async (def) => {\n if (!def.dataFn) return undefined;\n\n const dataFn = def.dataFn as unknown as DataFn<RouteParams, TRoutes>;\n\n try {\n return await dataFn({ ...context, signal } as DataContext<RouteParams, TRoutes>);\n } catch (err) {\n if (def.onError) return def.onError(err, { ...context, signal } as unknown as DataContext);\n\n throw err;\n }\n }),\n );\n }\n\n // ─── Private: URL resolution ──────────────────────────────────────────────\n\n /** Parse a URL, call #prepareRoute, and follow declarative redirects up to 5 hops. */\n async #resolveUrl(url: string): Promise<NavigationDecision> {\n let destination = url;\n\n for (let i = 0; i < 5; i += 1) {\n const parsed = new URL(destination, 'http://localhost');\n const location: RouteLocation = {\n hash: parsed.hash.replace(/^#/, ''),\n historyState: null,\n pathname: stripBase(parsed.pathname, this.#base),\n query: parseQuery(parsed.search),\n };\n\n const prepared = await this.#prepareRoute(location);\n\n if (prepared.type !== 'redirect') return prepared;\n\n destination = this.#resolveDestination(prepared.redirectTo);\n }\n\n throw new WayfinderRedirectLoopError();\n }\n\n // ─── Private: route preparation ───────────────────────────────────────────\n\n async #prepareRoute(location: RouteLocation): Promise<NavigationDecision> {\n const { params, record } = matchRouteFor(location.pathname, this.#records);\n\n if (!record) {\n return { location, params, type: 'unmatched' };\n }\n\n if (record.redirect) {\n return {\n location,\n params,\n redirectTo: resolveTarget(record.redirect, this.#routesByName, this.#base),\n type: 'redirect',\n };\n }\n\n let resolvedQuery: ResolvedQueryParams = location.query;\n\n const coerce: CoerceSearchFn | undefined = record.coerceSearch ?? this.#globalCoerceSearch;\n\n if (coerce) {\n try {\n resolvedQuery = coerce(location.query);\n } catch (err) {\n this.#reportError(err, { source: 'coerce-search' });\n resolvedQuery = location.query;\n }\n }\n\n return {\n branch: buildMatchBranch(\n record.branchDefs,\n params,\n location.pathname,\n record.branchDefs.map(() => undefined),\n ),\n location,\n params,\n record,\n resolvedQuery,\n type: 'matched',\n };\n }\n\n // ─── Private: terminal (data only) ────────────────────────────────────────\n\n async #runTerminal(\n record: RouteRecord,\n context: RouteContext<RouteParams, TRoutes>,\n location: RouteLocation,\n params: RouteParams,\n signal: AbortSignal,\n isCurrent: () => boolean,\n onDataError?: (error: unknown) => void,\n ): Promise<void> {\n if (!isCurrent()) return;\n\n const defs = record.branchDefs;\n const hasData = defs.some((d) => d.dataFn != null);\n let dataResults: unknown[] = defs.map(() => undefined);\n\n if (hasData) {\n const preloadKey = this.#hrefForLocation(location);\n const preloading = this.#preloads.get(preloadKey);\n\n if (preloading) {\n await preloading.catch(() => undefined);\n\n if (!isCurrent()) return;\n }\n\n const cached = this.#preloaded.get(preloadKey);\n\n if (cached) {\n this.#preloaded.delete(preloadKey);\n dataResults = cached;\n } else {\n this.#currentState = createRouteState({\n location,\n matches: buildMatchBranch(defs, params, location.pathname, dataResults),\n status: 'loading',\n });\n this.#notifyListeners();\n\n try {\n dataResults = await this.#loadData(defs, context, signal);\n } catch (error) {\n if (!isCurrent()) return;\n\n this.#currentState = createRouteState({\n error,\n location,\n matches: buildMatchBranch(defs, params, location.pathname, dataResults),\n status: 'error',\n });\n onDataError?.(error);\n throw error;\n }\n }\n }\n\n if (!isCurrent()) return;\n\n this.#currentState = createRouteState({\n location,\n matches: buildMatchBranch(defs, params, location.pathname, dataResults),\n status: 'idle',\n });\n }\n\n // ─── Private: main navigation orchestrator ────────────────────────────────\n\n /**\n * Declarative redirects reuse the active attempt and replace the final history entry.\n * Middleware redirects call `ctx.navigate()`, which starts a new attempt.\n */\n async #handleRoute(\n attempt: NavigationAttempt,\n currentLocation: RouteLocation,\n commit: (location: RouteLocation, replace: boolean) => void,\n useTransition?: boolean,\n depth = 0,\n replace = false,\n ): Promise<boolean> {\n const previousState = this.#currentState;\n const isCurrent = (): boolean => attempt.isCurrent() && !this.#disposed;\n const prepared = await this.#prepareRoute(currentLocation);\n\n if (!isCurrent()) return false;\n\n if (prepared.type === 'redirect') {\n if (depth >= 5) throw new WayfinderRedirectLoopError();\n\n return this.#handleRoute(\n attempt,\n this.#locationFromPath(prepared.redirectTo, currentLocation.historyState),\n commit,\n useTransition,\n depth + 1,\n true,\n );\n }\n\n if (prepared.type === 'unmatched') {\n // Fall back to notFound record when defined.\n if (this.#notFoundRecord) {\n const nfDefs = [this.#notFoundRecord.leaf];\n const nfBranch = buildMatchBranch(nfDefs, {}, currentLocation.pathname, [undefined]);\n let committed = false;\n let dataError: unknown;\n let hasDataError = false;\n let terminalRan = false;\n\n // Apply global coerceSearch to the unmatched location so notFound handlers\n // receive typed query params, consistent with matched-route behaviour.\n let nfResolvedQuery: ResolvedQueryParams = currentLocation.query;\n\n if (this.#globalCoerceSearch) {\n try {\n nfResolvedQuery = this.#globalCoerceSearch(currentLocation.query);\n } catch (err) {\n this.#reportError(err, { source: 'coerce-search' });\n }\n }\n\n const run = async (): Promise<void> => {\n if (!isCurrent()) return;\n\n const context = createRouteContext<TRoutes>(\n currentLocation,\n nfResolvedQuery,\n {},\n nfBranch,\n (target, options) => this.navigate(target, options),\n );\n\n terminalRan = await executeMiddlewarePipeline(\n context,\n [...this.#globalMiddleware, ...this.#notFoundRecord!.ownMiddleware] as unknown as Middleware<TRoutes>[],\n async () => {\n if (!isCurrent()) return;\n\n committed = true;\n commit(currentLocation, replace);\n\n if (!isCurrent()) return;\n\n await this.#runTerminal(\n this.#notFoundRecord!,\n context,\n currentLocation,\n {},\n attempt.signal,\n isCurrent,\n (error) => {\n dataError = error;\n hasDataError = true;\n },\n );\n },\n (error) =>\n isRouterErrorCarrier(error) || (hasDataError && error === dataError)\n ? error\n : carryError(error, { routeName: '__notFound__', source: 'middleware' }),\n );\n };\n\n try {\n await this.#runWithTransition(run, useTransition);\n } catch (error) {\n if (hasDataError && error === dataError) {\n throw carryError(error, { routeName: '__notFound__', source: 'data-loader' });\n }\n\n throw error;\n } finally {\n if (isCurrent() && committed) {\n this.#notifyListeners();\n this.#applyScroll(this.#currentState, previousState);\n }\n }\n\n return terminalRan && committed;\n }\n\n if (!isCurrent()) return false;\n\n commit(prepared.location, replace);\n\n if (!isCurrent()) return false;\n\n this.#currentState = createRouteState({\n location: prepared.location,\n matches: [] as RouteMatchBranch,\n status: 'idle',\n });\n this.#notifyListeners();\n this.#applyScroll(this.#currentState, previousState);\n\n return true;\n }\n\n const { branch, location, params, record, resolvedQuery } = prepared;\n let committed = false;\n let dataError: unknown;\n let hasDataError = false;\n let terminalRan = false;\n\n const run = async (): Promise<void> => {\n if (!isCurrent()) return;\n\n const context = createRouteContext<TRoutes>(location, resolvedQuery, params, branch, (target, options) =>\n this.navigate(target, options),\n );\n\n terminalRan = await executeMiddlewarePipeline(\n context,\n [...this.#globalMiddleware, ...record.ownMiddleware] as unknown as Middleware<TRoutes>[],\n async () => {\n if (!isCurrent()) return;\n\n committed = true;\n commit(location, replace);\n\n if (!isCurrent()) return;\n\n await this.#runTerminal(record, context, location, params, attempt.signal, isCurrent, (error) => {\n dataError = error;\n hasDataError = true;\n });\n },\n (error) =>\n isRouterErrorCarrier(error) || (hasDataError && error === dataError)\n ? error\n : carryError(error, { routeName: record.leaf.name, source: 'middleware' }),\n );\n };\n\n try {\n await this.#runWithTransition(run, useTransition);\n } catch (error) {\n if (hasDataError && error === dataError) {\n throw carryError(error, { routeName: record.leaf.name, source: 'data-loader' });\n }\n\n throw error;\n } finally {\n if (isCurrent() && committed) {\n this.#notifyListeners();\n this.#applyScroll(this.#currentState, previousState);\n }\n }\n\n return terminalRan && committed;\n }\n\n async #runWithTransition(run: () => Promise<void>, useTransition?: boolean): Promise<void> {\n if ((useTransition ?? this.#useViewTransition) && typeof document !== 'undefined') {\n const startViewTransition = (\n document as Document & {\n startViewTransition?: (callback: () => void | Promise<void>) => { finished: Promise<void> };\n }\n ).startViewTransition;\n\n if (startViewTransition) {\n await startViewTransition.call(document, run).finished;\n\n return;\n }\n }\n\n await run();\n }\n\n #applyScroll(to: RouteState, from: RouteState): void {\n if (!this.#scroll || typeof window === 'undefined') return;\n\n const decision = this.#scroll(to, from);\n\n if (decision === 'preserve') return;\n\n if (decision === 'top') {\n window.scrollTo(0, 0);\n\n return;\n }\n\n window.scrollTo(decision.x, decision.y);\n }\n\n // ─── Private: destination resolution ─────────────────────────────────────\n\n #resolveDestination(path: string): string {\n const parsed = new URL(path, 'http://localhost');\n const normalizedPath = stripBase(parsed.pathname, this.#base);\n\n return `${joinPaths(this.#base, normalizedPath)}${parsed.search}${parsed.hash}`;\n }\n\n #locationFromPath(path: string, historyState: unknown): RouteLocation {\n const parsed = new URL(this.#resolveDestination(path), 'http://localhost');\n\n return {\n hash: parsed.hash.replace(/^#/, ''),\n historyState,\n pathname: stripBase(parsed.pathname, this.#base),\n query: parseQuery(parsed.search),\n };\n }\n\n #hrefForLocation(location: RouteLocation): string {\n const search = new URLSearchParams();\n\n for (const [key, value] of Object.entries(location.query)) {\n if (Array.isArray(value))\n value.forEach((item) => {\n search.append(key, item);\n });\n else search.set(key, value);\n }\n\n const query = search.toString();\n const hash = location.hash ? `#${location.hash}` : '';\n\n return `${joinPaths(this.#base, location.pathname)}${query ? `?${query}` : ''}${hash}`;\n }\n\n #navigationDestination(href: string): NavigationDestination {\n const parsed = new URL(href, 'http://localhost');\n const pathname = stripBase(parsed.pathname, this.#base);\n const { params, record } = matchRouteFor(pathname, this.#records);\n\n return {\n name: record?.leaf.name,\n params,\n pathname,\n query: parseQuery(parsed.search),\n };\n }\n\n /** User-initiated navigation. History changes only after middleware reaches the terminal stage. */\n async #navigateToPath(path: string, options: NavigateOptions = {}): Promise<void> {\n this.#assertNotDisposed();\n\n const destination = this.#resolveDestination(path);\n\n if (!options.force && destination === this.#lastHref) return;\n\n const attempt = this.#navigation.begin();\n const activeMatchNames = this.#currentState.matches.map((match) => match.name);\n const allowed = await runLeaveBlockers(\n this.#beforeLeaveBlockers,\n activeMatchNames,\n this.#navigationDestination(destination),\n );\n\n if (!attempt.isCurrent() || !allowed) return;\n\n try {\n await this.#handleRoute(\n attempt,\n this.#locationFromPath(destination, options.state),\n (location, replace) => {\n if (!attempt.isCurrent()) return;\n\n const href = this.#hrefForLocation(location);\n\n if (replace) this.#history.replace(href, location.historyState);\n else this.#history.push(href, location.historyState);\n\n this.#lastHref = href;\n },\n options.viewTransition,\n 0,\n options.replace ?? false,\n );\n } catch (value) {\n throw isRouterErrorCarrier(value) ? value.error : value;\n }\n }\n}\n\n// ─── Public factory ───────────────────────────────────────────────────────────\n\n/**\n * Create a new router instance from a route table.\n *\n * @example\n * const router = createRouter({\n * routes: {\n * home: { path: '/' },\n * userDetail: { path: '/users/:id', data: fetchUser },\n * },\n * });\n */\nexport function createRouter<const TRoutes extends RouteTable>(options: RouterOptions<TRoutes>): Router<TRoutes> {\n return new Router(options);\n}\n\n// Export the Router type (not the constructor value) for type annotations.\nexport type { Router };\n"],"mappings":"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"}
|
package/dist/wayfinder.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var e=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},t=class extends e{constructor(){super(`Router is disposed`)}},n=class extends e{},r=class extends e{constructor(){super(`Redirect loop detected`)}},i=class extends e{};function a(){return{back(){window.history.back()},get location(){return{hash:window.location.hash,pathname:window.location.pathname,search:window.location.search,state:window.history.state}},onPopstate(e){return window.addEventListener(`popstate`,e),()=>window.removeEventListener(`popstate`,e)},push(e,t){window.history.pushState(t,``,e)},replace(e,t){window.history.replaceState(t,``,e)}}}function o(e=`/`){let t=new URL(e,`http://localhost`),n=[{hash:t.hash,pathname:t.pathname,search:t.search,state:null}],r=0,i=new Set;return{back(){r<=0||(--r,i.forEach(e=>{e()}))},get location(){return n[r]},onPopstate(e){return i.add(e),()=>i.delete(e)},push(e,t=null){let i=new URL(e,`http://localhost`);n.splice(r+1),n.push({hash:i.hash,pathname:i.pathname,search:i.search,state:t}),r=n.length-1},replace(e,t=null){let i=new URL(e,`http://localhost`);n[r]={hash:i.hash,pathname:i.pathname,search:i.search,state:t}}}}function s(e,t){return async n=>{await n.navigate(e,t)}}function c(){let e;return{begin(){e?.abort();let t=new AbortController;return e=t,{isCurrent:()=>e===t&&!t.signal.aborted,signal:t.signal}},invalidate(t){e?.abort(t),e=void 0}}}function l(e){let t=`/${e}`.replace(/\/+/g,`/`);return t!==`/`&&t.endsWith(`/`)?t.slice(0,-1):t}function u(e,t){return l(`${e}/${t}`)}function d(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function f(e){let t=l(e);if(t===`/`)return{paramNames:[],pattern:/^\/$/,prefixPattern:/^\/$/};let r=[],i=t.slice(1).split(`/`),a=[`^`],o=[`^`];for(let t=0;t<i.length;t+=1){let s=i[t];if(s===`*`){if(t!==i.length-1)throw new n(`Wildcard "*" must be the final segment in path: ${e}`);a.push(`(?:/.*)?`);break}if(s.startsWith(`:`)&&s.endsWith(`*`)){if(t!==i.length-1)throw new n(`Wildcard param must be final segment in path: ${e}`);let c=s.slice(1,-1);if(!/^\w+$/.test(c))throw new n(`Invalid param name ":${c}" in path "${e}". Param names must only contain word characters (a–z, A–Z, 0–9, _).`);r.push(c),a.push(t===i.length-1?`(?:/(.*))?`:`/(.*)`),o.push(`/.*`);break}if(s.startsWith(`:`)){let t=s.slice(1);if(!/^\w+$/.test(t))throw new n(`Invalid param name ":${t}" in path "${e}". Param names must only contain word characters (a–z, A–Z, 0–9, _).`);r.push(t),a.push(`/([^/]+)`),o.push(`/[^/]+`);continue}let c=d(s);a.push(`/${c}`),o.push(`/${c}`)}return a.push(`$`),o.push(`(?:/.*)?$`),{paramNames:r,pattern:new RegExp(a.join(``)),prefixPattern:new RegExp(o.join(``))}}function p(e,t){let n=t.matcher.pattern.exec(e);if(!n)return null;let r={};return t.matcher.paramNames.forEach((e,t)=>{let i=n[t+1]??``;try{r[e]=decodeURIComponent(i)}catch{r[e]=i}}),r}function m(e,t){for(let n of t){let t=p(e,n);if(t)return{params:t,record:n}}return{params:{}}}function h(e,t){return t.matcher.prefixPattern.test(e)}function g(e){let t=new URLSearchParams(e),n={},r=(e,t)=>{Object.defineProperty(n,e,{configurable:!0,enumerable:!0,value:t,writable:!0})};for(let[e,i]of t.entries()){let t=Object.hasOwn(n,e)?n[e]:void 0;if(t===void 0){r(e,i);continue}if(Array.isArray(t)){t.push(i);continue}r(e,[t,i])}return n}function _(e,t,r={},i){let a=t.replace(/:(\w+)(\*)?/g,(e,t,i)=>{let a=r[t];if(a==null)throw new n(`Missing path param: ${t}`);return i?a.split(`/`).map(e=>encodeURIComponent(e)).join(`/`):encodeURIComponent(a)});if(i&&Object.keys(i).length){let e=new URLSearchParams;Object.entries(i).forEach(([t,n])=>{Array.isArray(n)?n.forEach(n=>{e.append(t,String(n))}):e.set(t,String(n))}),a+=`?${e.toString()}`}return u(e,a)}function v(e,t=`/`){let n=l(e),r=l(t);if(r===`/`)return n;if(n===r)return`/`;let i=`${r}/`;return n.startsWith(i)?l(n.slice(r.length)):n}function y(e,t){return{hash:t.location.hash.replace(/^#/,``),historyState:t.location.state,pathname:v(t.location.pathname||`/`,e),query:g(t.location.search||``)}}function b(e){let t=[],r=(e,i,a,o,s)=>{if(i.index&&i.path!==void 0)throw new n(`Route "${e}" cannot define both index and path`);if(!i.index&&i.path===void 0)throw new n(`Route "${e}" must define path or set index: true`);let c=i.index?a:l(i.path?u(a,i.path):a),d=[...o,{dataFn:i.data,name:e,onError:i.onError}],p=[...s,...i.middleware??[]];if(i.children)for(let[t,n]of Object.entries(i.children))r(`${e}.${t}`,n,c,d,p);if(i.redirect!==void 0||!i.children||i.data!==void 0){let e=d[d.length-1];t.push({branchDefs:d,coerceSearch:i.coerceSearch,leaf:e,matcher:f(c),ownMiddleware:p,path:c,redirect:i.redirect})}};for(let[t,n]of Object.entries(e.routes))r(t,n,`/`,[],[]);let i=new Set;for(let e of t){if(i.has(e.leaf.name))throw new n(`Duplicate route name: "${e.leaf.name}". A top-level route key must not coincide with a nested route's dot-notation name.`);i.add(e.leaf.name)}let a=!1;for(let e of t){let t=e.path===`/*`||e.path.endsWith(`/*`)||e.path.includes(`:`)&&e.path.endsWith(`*`);a&&!t&&`${e.leaf.name}${e.path}`,t&&(a=!0)}return{records:t,routesByName:new Map(t.map(e=>[e.leaf.name,e]))}}function x(e){return{error:e.error,location:e.location,matches:e.matches,status:e.status}}function S(e,t,n,r){return e.map((e,i)=>({data:r[i],name:e.name,params:{...t},pathname:n}))}function C(e,t,n,r,i){return{hash:e.hash,historyState:e.historyState,locals:{},matches:r,navigate:i,params:n,pathname:e.pathname,query:t}}async function w(e,t,n,r){let a=!1;async function o(s){if(s<t.length){let n=!1;try{await t[s]?.(e,async()=>{if(n)throw new i(`next() called multiple times`);n=!0,await o(s+1)})}catch(e){throw r?.(e)??e}return}a=!0,await n()}return await o(0),a}function T(e,t,n){if(n){n(e,t);return}}async function E(e,t,n){let r=new Set(t),i=[...e];for(let{handler:e,routes:t}of i)if((t===void 0||t.some(e=>r.has(e)))&&!await e(n))return!1;return!0}function D(e,t){let r=t.get(e);if(r)return r;let i=[...t.keys()].join(`, `);throw new n(i?`Unknown route name: ${e}. Available routes: ${i}`:`Unknown route name: ${e}`)}function O(e,t,n=`/`){if(`path`in e)return e.path;let r=_(n,D(e.name,t).path,e.params,e.query);return e.hash?`${r}#${e.hash}`:r}var k=Symbol(`wayfinder.routerError`);function A(e,t){return{[k]:!0,context:t,error:e}}function j(e){return typeof e==`object`&&!!e&&k in e}function M(e){throw j(e)?e.error:e}var N=class{#e;#t;#n;#r;#i;#a;#o;#s;#c;#l=new Set;#u=c();#d;#f=!1;#p=new AbortController;#m=`/`;#h=new Set;#g=new Map;#_=new Map;#v;#y;ready;constructor(e){let t=b(e);if(this.#e=l(e.base??`/`),this.#t=e.middleware??[],this.#n=e.coerceSearch,this.#r=e.history??a(),this.#o=e.onError,this.#s=e.scroll,this.#c=e.viewTransition??!1,this.#i=t.records,this.#a=t.routesByName,this.#d=x({location:{hash:``,historyState:null,pathname:`/`,query:{}},matches:[],status:`idle`}),e.notFound){let t=e.notFound,n={dataFn:t.data,name:`__notFound__`};this.#v={branchDefs:[n],leaf:n,matcher:{paramNames:[],pattern:/(?:)/,prefixPattern:/(?:)/},ownMiddleware:t.middleware??[],path:`/*`}}else this.#v=null;this.#y=this.#x();let{hash:n,pathname:r,search:i}=this.#r.location;this.#m=`${r}${i}${n}`;let o=this.#u.begin(),s=this.#A(o,y(this.#e,this.#r),(e,t)=>{if(!o.isCurrent())return;let n=this.#F(e);t&&this.#r.replace(n,e.historyState),this.#m=n},void 0,0,!1).then(()=>void 0);this.ready=s.catch(M),this.ready.catch(()=>{}),this.#w(s,{source:`initial-navigation`}),this.beforeLeave=this.beforeLeave.bind(this),this.createViewRegistry=this.createViewRegistry.bind(this),this.dispose=this.dispose.bind(this),this.getSnapshot=this.getSnapshot.bind(this),this.isActive=this.isActive.bind(this),this.load=this.load.bind(this),this.match=this.match.bind(this),this.navigate=this.navigate.bind(this),this.preload=this.preload.bind(this),this.subscribe=this.subscribe.bind(this),this.url=this.url.bind(this),this.waitFor=this.waitFor.bind(this)}getSnapshot(){return this.#d}createViewRegistry(e,t){return{resolve:n=>{let r=n.matches.at(-1)?.name;return r===`__notFound__`?t?.notFound:r?e[r]:void 0}}}subscribe(e){return this.#b(),this.#h.add(e),()=>{this.#h.delete(e)}}navigate(e,t){let n=O(typeof e==`string`?{path:e}:e,this.#a);return this.#L(n,t)}url(e,t,n){let r=D(e,this.#a);return _(this.#e,r.path,t,n)}isActive(e,t={}){let n=D(e,this.#a),r=this.#d.location.pathname;return t.exact??!1?m(r,[n]).record!=null:h(r,n)}match(e){let t=v(l(e),this.#e),{params:n,record:r}=m(t,this.#i);if(!r||r.redirect)return null;let i=S(r.branchDefs,n,t,r.branchDefs.map(()=>void 0));return i.length?i:null}async load(e,t){let n=await this.#D(e);if(n.type!==`matched`)return null;let{location:r,params:i,record:a,resolvedQuery:o}=n,s=a.branchDefs,c=s.some(e=>e.dataFn!=null),l=s.map(()=>void 0),u,d=`idle`;if(c){let e=t?.signal??new AbortController().signal,n=C(r,o,i,S(s,i,r.pathname,l),()=>Promise.resolve());try{l=await this.#E(s,n,e)}catch(e){u=e,d=`error`}}return x({error:u,location:r,matches:S(s,i,r.pathname,l),status:d})}async preload(e){this.#b();let t=this.#N(O(e,this.#a,this.#e)),n=this.#_.get(t);if(n)return n;let r=this.load(t,{signal:this.#p.signal}).then(e=>{if(this.#f)throw this.#p.signal.reason;if(e?.status===`error`)throw e.error;if(e){let t=this.#F(e.location);this.#g.size>=20&&this.#g.delete(this.#g.keys().next().value),this.#g.set(t,e.matches.map(e=>e.data))}return e}).catch(e=>{throw this.#o&&this.#C(e,{source:`preload`}),e}).finally(()=>{this.#_.delete(t)});return this.#_.set(t,r),r}waitFor(e){return this.#b(),new Promise((t,n)=>{let r=t=>t.status===`idle`&&t.matches.some(t=>t.name===e);if(this.#d.status===`error`){n(this.#d.error);return}if(r(this.#d)){t(this.#d);return}let i=this.subscribe(e=>{r(e)?(i(),t(e)):e.status===`error`&&(i(),n(e.error))});this.#p.signal.addEventListener(`abort`,()=>{i(),n(this.#p.signal.reason)},{once:!0})})}beforeLeave(e,t){this.#b();let n={handler:e,routes:t?.routes};return this.#l.add(n),()=>{this.#l.delete(n)}}get disposalSignal(){return this.#p.signal}get disposed(){return this.#f}dispose(){this.#f||(this.#f=!0,this.#l.clear(),this.#h.clear(),this.#g.clear(),this.#_.clear(),this.#u.invalidate(new t),this.#y(),this.#p.abort(new t))}[Symbol.dispose](){this.dispose()}#b(){if(this.#f)throw new t}#x(){return this.#r.onPopstate(()=>{let{hash:e,pathname:t,search:n}=this.#r.location,r=`${t}${n}${e}`,i=this.#m;r!==i&&this.#w(this.#S(r,i),{source:`history-listener`})})}async#S(e,t){let n=this.#u.begin(),r=this.#d.matches.map(e=>e.name),i=this.#I(e),a=await E(this.#l,r,i);if(!n.isCurrent())return;if(!a){this.#r.replace(t,this.#d.location.historyState);return}let o=await this.#A(n,y(this.#e,this.#r),(e,t)=>{if(!n.isCurrent())return;let r=this.#F(e);t&&this.#r.replace(r,e.historyState),this.#m=r},void 0,0,!1);n.isCurrent()&&!o&&this.#r.replace(t,this.#d.location.historyState)}#C(e,t){T(e,t,this.#o)}#w(e,t){e.catch(e=>{j(e)?this.#C(e.error,e.context):this.#C(e,t)})}#T(){this.#h.forEach(e=>{e(this.#d)})}async#E(e,t,n){return Promise.all(e.map(async e=>{if(!e.dataFn)return;let r=e.dataFn;try{return await r({...t,signal:n})}catch(r){if(e.onError)return e.onError(r,{...t,signal:n});throw r}}))}async#D(e){let t=e;for(let e=0;e<5;e+=1){let e=new URL(t,`http://localhost`),n={hash:e.hash.replace(/^#/,``),historyState:null,pathname:v(e.pathname,this.#e),query:g(e.search)},r=await this.#O(n);if(r.type!==`redirect`)return r;t=this.#N(r.redirectTo)}throw new r}async#O(e){let{params:t,record:n}=m(e.pathname,this.#i);if(!n)return{location:e,params:t,type:`unmatched`};if(n.redirect)return{location:e,params:t,redirectTo:O(n.redirect,this.#a,this.#e),type:`redirect`};let r=e.query,i=n.coerceSearch??this.#n;if(i)try{r=i(e.query)}catch(t){this.#C(t,{source:`coerce-search`}),r=e.query}return{branch:S(n.branchDefs,t,e.pathname,n.branchDefs.map(()=>void 0)),location:e,params:t,record:n,resolvedQuery:r,type:`matched`}}async#k(e,t,n,r,i,a,o){if(!a())return;let s=e.branchDefs,c=s.some(e=>e.dataFn!=null),l=s.map(()=>void 0);if(c){let e=this.#F(n),c=this.#_.get(e);if(c&&(await c.catch(()=>void 0),!a()))return;let u=this.#g.get(e);if(u)this.#g.delete(e),l=u;else{this.#d=x({location:n,matches:S(s,r,n.pathname,l),status:`loading`}),this.#T();try{l=await this.#E(s,t,i)}catch(e){if(!a())return;throw this.#d=x({error:e,location:n,matches:S(s,r,n.pathname,l),status:`error`}),o?.(e),e}}}a()&&(this.#d=x({location:n,matches:S(s,r,n.pathname,l),status:`idle`}))}async#A(e,t,n,i,a=0,o=!1){let s=this.#d,c=()=>e.isCurrent()&&!this.#f,l=await this.#O(t);if(!c())return!1;if(l.type===`redirect`){if(a>=5)throw new r;return this.#A(e,this.#P(l.redirectTo,t.historyState),n,i,a+1,!0)}if(l.type===`unmatched`){if(this.#v){let r=S([this.#v.leaf],{},t.pathname,[void 0]),a=!1,l,u=!1,d=!1,f=t.query;if(this.#n)try{f=this.#n(t.query)}catch(e){this.#C(e,{source:`coerce-search`})}let p=async()=>{if(!c())return;let i=C(t,f,{},r,(e,t)=>this.navigate(e,t));d=await w(i,[...this.#t,...this.#v.ownMiddleware],async()=>{c()&&(a=!0,n(t,o),c()&&await this.#k(this.#v,i,t,{},e.signal,c,e=>{l=e,u=!0}))},e=>j(e)||u&&e===l?e:A(e,{routeName:`__notFound__`,source:`middleware`}))};try{await this.#j(p,i)}catch(e){throw u&&e===l?A(e,{routeName:`__notFound__`,source:`data-loader`}):e}finally{c()&&a&&(this.#T(),this.#M(this.#d,s))}return d&&a}return!c()||(n(l.location,o),!c())?!1:(this.#d=x({location:l.location,matches:[],status:`idle`}),this.#T(),this.#M(this.#d,s),!0)}let{branch:u,location:d,params:f,record:p,resolvedQuery:m}=l,h=!1,g,_=!1,v=!1,y=async()=>{if(!c())return;let t=C(d,m,f,u,(e,t)=>this.navigate(e,t));v=await w(t,[...this.#t,...p.ownMiddleware],async()=>{c()&&(h=!0,n(d,o),c()&&await this.#k(p,t,d,f,e.signal,c,e=>{g=e,_=!0}))},e=>j(e)||_&&e===g?e:A(e,{routeName:p.leaf.name,source:`middleware`}))};try{await this.#j(y,i)}catch(e){throw _&&e===g?A(e,{routeName:p.leaf.name,source:`data-loader`}):e}finally{c()&&h&&(this.#T(),this.#M(this.#d,s))}return v&&h}async#j(e,t){if((t??this.#c)&&typeof document<`u`){let t=document.startViewTransition;if(t){await t.call(document,e).finished;return}}await e()}#M(e,t){if(!this.#s||typeof window>`u`)return;let n=this.#s(e,t);if(n!==`preserve`){if(n===`top`){window.scrollTo(0,0);return}window.scrollTo(n.x,n.y)}}#N(e){let t=new URL(e,`http://localhost`),n=v(t.pathname,this.#e);return`${u(this.#e,n)}${t.search}${t.hash}`}#P(e,t){let n=new URL(this.#N(e),`http://localhost`);return{hash:n.hash.replace(/^#/,``),historyState:t,pathname:v(n.pathname,this.#e),query:g(n.search)}}#F(e){let t=new URLSearchParams;for(let[n,r]of Object.entries(e.query))Array.isArray(r)?r.forEach(e=>{t.append(n,e)}):t.set(n,r);let n=t.toString(),r=e.hash?`#${e.hash}`:``;return`${u(this.#e,e.pathname)}${n?`?${n}`:``}${r}`}#I(e){let t=new URL(e,`http://localhost`),n=v(t.pathname,this.#e),{params:r,record:i}=m(n,this.#i);return{name:i?.leaf.name,params:r,pathname:n,query:g(t.search)}}async#L(e,t={}){this.#b();let n=this.#N(e);if(!t.force&&n===this.#m)return;let r=this.#u.begin(),i=this.#d.matches.map(e=>e.name),a=await E(this.#l,i,this.#I(n));if(r.isCurrent()&&a)try{await this.#A(r,this.#P(n,t.state),(e,t)=>{if(!r.isCurrent())return;let n=this.#F(e);t?this.#r.replace(n,e.historyState):this.#r.push(n,e.historyState),this.#m=n},t.viewTransition,0,t.replace??!1)}catch(e){throw j(e)?e.error:e}}};function P(e){return new N(e)}export{i as WayfinderApiError,t as WayfinderDisposedError,e as WayfinderError,r as WayfinderRedirectLoopError,n as WayfinderRouteError,a as createBrowserHistory,o as createMemoryHistory,P as createRouter,s as redirectTo};
|
|
1
|
+
var e=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},t=class extends e{constructor(){super(`Router is disposed`)}},n=class extends e{},r=class extends e{constructor(){super(`Redirect loop detected`)}},i=class extends e{};function a(e){let t=`/${e}`.replace(/\/+/g,`/`);return t!==`/`&&t.endsWith(`/`)?t.slice(0,-1):t}function o(e,t){return a(`${e}/${t}`)}function s(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function c(e){let t=a(e);if(t===`/`)return{paramNames:[],pattern:/^\/$/,prefixPattern:/^\/$/};let r=[],i=t.slice(1).split(`/`),o=[`^`],c=[`^`];for(let t=0;t<i.length;t+=1){let a=i[t];if(a===`*`){if(t!==i.length-1)throw new n(`Wildcard "*" must be the final segment in path: ${e}`);o.push(`(?:/.*)?`);break}if(a.startsWith(`:`)&&a.endsWith(`*`)){if(t!==i.length-1)throw new n(`Wildcard param must be final segment in path: ${e}`);let s=a.slice(1,-1);if(!/^\w+$/.test(s))throw new n(`Invalid param name ":${s}" in path "${e}". Param names must only contain word characters (a–z, A–Z, 0–9, _).`);r.push(s),o.push(t===i.length-1?`(?:/(.*))?`:`/(.*)`),c.push(`/.*`);break}if(a.startsWith(`:`)){let t=a.slice(1);if(!/^\w+$/.test(t))throw new n(`Invalid param name ":${t}" in path "${e}". Param names must only contain word characters (a–z, A–Z, 0–9, _).`);r.push(t),o.push(`/([^/]+)`),c.push(`/[^/]+`);continue}let l=s(a);o.push(`/${l}`),c.push(`/${l}`)}return o.push(`$`),c.push(`(?:/.*)?$`),{paramNames:r,pattern:new RegExp(o.join(``)),prefixPattern:new RegExp(c.join(``))}}function l(e,t){let n=t.matcher.pattern.exec(e);if(!n)return null;let r={};return t.matcher.paramNames.forEach((e,t)=>{let i=n[t+1]??``;try{r[e]=decodeURIComponent(i)}catch{r[e]=i}}),r}function u(e,t){for(let n of t){let t=l(e,n);if(t)return{params:t,record:n}}return{params:{}}}function d(e,t){return t.matcher.prefixPattern.test(e)}function f(e){let t=new URLSearchParams(e),n={},r=(e,t)=>{Object.defineProperty(n,e,{configurable:!0,enumerable:!0,value:t,writable:!0})};for(let[e,i]of t.entries()){let t=Object.hasOwn(n,e)?n[e]:void 0;if(t===void 0){r(e,i);continue}if(Array.isArray(t)){t.push(i);continue}r(e,[t,i])}return n}function p(e,t,r={},i){let a=t.replace(/:(\w+)(\*)?/g,(e,t,i)=>{let a=r[t];if(a==null)throw new n(`Missing path param: ${t}`);return i?a.split(`/`).map(e=>encodeURIComponent(e)).join(`/`):encodeURIComponent(a)});if(i&&Object.keys(i).length){let e=new URLSearchParams;Object.entries(i).forEach(([t,n])=>{Array.isArray(n)?n.forEach(n=>{e.append(t,String(n))}):e.set(t,String(n))}),a+=`?${e.toString()}`}return o(e,a)}function m(e,t=`/`){let n=a(e),r=a(t);if(r===`/`)return n;if(n===r)return`/`;let i=`${r}/`;return n.startsWith(i)?a(n.slice(r.length)):n}function h(e,t){return{hash:t.location.hash.replace(/^#/,``),historyState:t.location.state,pathname:m(t.location.pathname||`/`,e),query:f(t.location.search||``)}}function g(){return{back(){window.history.back()},get location(){return{hash:window.location.hash,pathname:window.location.pathname,search:window.location.search,state:window.history.state}},onPopstate(e){return window.addEventListener(`popstate`,e),()=>window.removeEventListener(`popstate`,e)},push(e,t){window.history.pushState(t,``,e)},replace(e,t){window.history.replaceState(t,``,e)}}}function _(e={}){let t=a(e.base??`/`),n=t===`/`?`/`:`${t}/`,r=()=>{let e=new URL(window.location.hash.slice(1)||`/`,`http://localhost`);return{hash:e.hash,pathname:o(t,e.pathname),search:e.search,state:window.history.state}},i=e=>{let r=new URL(e,`http://localhost`),i=m(r.pathname,t);return`${n}#${i}${r.search}${r.hash}`};return{back(){window.history.back()},get location(){return r()},onPopstate(e){return window.addEventListener(`popstate`,e),()=>window.removeEventListener(`popstate`,e)},push(e,t){window.history.pushState(t,``,i(e))},replace(e,t){window.history.replaceState(t,``,i(e))}}}function v(e=`/`){let t=new URL(e,`http://localhost`),n=[{hash:t.hash,pathname:t.pathname,search:t.search,state:null}],r=0,i=new Set;return{back(){r<=0||(--r,i.forEach(e=>{e()}))},get location(){return n[r]},onPopstate(e){return i.add(e),()=>i.delete(e)},push(e,t=null){let i=new URL(e,`http://localhost`);n.splice(r+1),n.push({hash:i.hash,pathname:i.pathname,search:i.search,state:t}),r=n.length-1},replace(e,t=null){let i=new URL(e,`http://localhost`);n[r]={hash:i.hash,pathname:i.pathname,search:i.search,state:t}}}}function y(e,t){return async n=>{await n.navigate(e,t)}}function b(){let e;return{begin(){e?.abort();let t=new AbortController;return e=t,{isCurrent:()=>e===t&&!t.signal.aborted,signal:t.signal}},invalidate(t){e?.abort(t),e=void 0}}}function x(e){let t=[],r=(e,i,s,l,u)=>{if(i.index&&i.path!==void 0)throw new n(`Route "${e}" cannot define both index and path`);if(!i.index&&i.path===void 0)throw new n(`Route "${e}" must define path or set index: true`);let d=i.index?s:a(i.path?o(s,i.path):s),f=[...l,{dataFn:i.data,name:e,onError:i.onError}],p=[...u,...i.middleware??[]];if(i.children)for(let[t,n]of Object.entries(i.children))r(`${e}.${t}`,n,d,f,p);if(i.redirect!==void 0||!i.children||i.data!==void 0){let e=f[f.length-1];t.push({branchDefs:f,coerceSearch:i.coerceSearch,leaf:e,matcher:c(d),ownMiddleware:p,path:d,redirect:i.redirect})}};for(let[t,n]of Object.entries(e.routes))r(t,n,`/`,[],[]);let i=new Set;for(let e of t){if(i.has(e.leaf.name))throw new n(`Duplicate route name: "${e.leaf.name}". A top-level route key must not coincide with a nested route's dot-notation name.`);i.add(e.leaf.name)}let s=!1;for(let e of t){let t=e.path===`/*`||e.path.endsWith(`/*`)||e.path.includes(`:`)&&e.path.endsWith(`*`);s&&!t&&`${e.leaf.name}${e.path}`,t&&(s=!0)}return{records:t,routesByName:new Map(t.map(e=>[e.leaf.name,e]))}}function S(e){return{error:e.error,location:e.location,matches:e.matches,status:e.status}}function C(e,t,n,r){return e.map((e,i)=>({data:r[i],name:e.name,params:{...t},pathname:n}))}function w(e,t,n,r,i){return{hash:e.hash,historyState:e.historyState,locals:{},matches:r,navigate:i,params:n,pathname:e.pathname,query:t}}async function T(e,t,n,r){let a=!1;async function o(s){if(s<t.length){let n=!1;try{await t[s]?.(e,async()=>{if(n)throw new i(`next() called multiple times`);n=!0,await o(s+1)})}catch(e){throw r?.(e)??e}return}a=!0,await n()}return await o(0),a}function E(e,t,n){if(n){n(e,t);return}}async function D(e,t,n){let r=new Set(t),i=[...e];for(let{handler:e,routes:t}of i)if((t===void 0||t.some(e=>r.has(e)))&&!await e(n))return!1;return!0}function O(e,t){let r=t.get(e);if(r)return r;let i=[...t.keys()].join(`, `);throw new n(i?`Unknown route name: ${e}. Available routes: ${i}`:`Unknown route name: ${e}`)}function k(e,t,n=`/`){if(`path`in e)return e.path;let r=p(n,O(e.name,t).path,e.params,e.query);return e.hash?`${r}#${e.hash}`:r}var A=Symbol(`wayfinder.routerError`);function j(e,t){return{[A]:!0,context:t,error:e}}function M(e){return typeof e==`object`&&!!e&&A in e}function N(e){throw M(e)?e.error:e}var P=class{#e;#t;#n;#r;#i;#a;#o;#s;#c;#l=new Set;#u=b();#d;#f=!1;#p=new AbortController;#m=`/`;#h=new Set;#g=new Map;#_=new Map;#v;#y;ready;constructor(e){let t=x(e);if(this.#e=a(e.base??`/`),this.#t=e.middleware??[],this.#n=e.coerceSearch,this.#r=e.history??g(),this.#o=e.onError,this.#s=e.scroll,this.#c=e.viewTransition??!1,this.#i=t.records,this.#a=t.routesByName,this.#d=S({location:{hash:``,historyState:null,pathname:`/`,query:{}},matches:[],status:`idle`}),e.notFound){let t=e.notFound,n={dataFn:t.data,name:`__notFound__`};this.#v={branchDefs:[n],leaf:n,matcher:{paramNames:[],pattern:/(?:)/,prefixPattern:/(?:)/},ownMiddleware:t.middleware??[],path:`/*`}}else this.#v=null;this.#y=this.#x();let{hash:n,pathname:r,search:i}=this.#r.location;this.#m=`${r}${i}${n}`;let o=this.#u.begin(),s=this.#A(o,h(this.#e,this.#r),(e,t)=>{if(!o.isCurrent())return;let n=this.#F(e);t&&this.#r.replace(n,e.historyState),this.#m=n},void 0,0,!1).then(()=>void 0);this.ready=s.catch(N),this.ready.catch(()=>{}),this.#w(s,{source:`initial-navigation`}),this.beforeLeave=this.beforeLeave.bind(this),this.createViewRegistry=this.createViewRegistry.bind(this),this.dispose=this.dispose.bind(this),this.getSnapshot=this.getSnapshot.bind(this),this.isActive=this.isActive.bind(this),this.load=this.load.bind(this),this.match=this.match.bind(this),this.navigate=this.navigate.bind(this),this.preload=this.preload.bind(this),this.subscribe=this.subscribe.bind(this),this.url=this.url.bind(this),this.waitFor=this.waitFor.bind(this)}getSnapshot(){return this.#d}createViewRegistry(e,t){return{resolve:n=>{let r=n.matches.at(-1)?.name;return r===`__notFound__`?t?.notFound:r?e[r]:void 0}}}subscribe(e){return this.#b(),this.#h.add(e),()=>{this.#h.delete(e)}}navigate(e,t){let n=k(typeof e==`string`?{path:e}:e,this.#a);return this.#L(n,t)}url(e,t,n){let r=O(e,this.#a);return p(this.#e,r.path,t,n)}isActive(e,t={}){let n=O(e,this.#a),r=this.#d.location.pathname;return t.exact??!1?u(r,[n]).record!=null:d(r,n)}match(e){let t=m(a(e),this.#e),{params:n,record:r}=u(t,this.#i);if(!r||r.redirect)return null;let i=C(r.branchDefs,n,t,r.branchDefs.map(()=>void 0));return i.length?i:null}async load(e,t){let n=await this.#D(e);if(n.type!==`matched`)return null;let{location:r,params:i,record:a,resolvedQuery:o}=n,s=a.branchDefs,c=s.some(e=>e.dataFn!=null),l=s.map(()=>void 0),u,d=`idle`;if(c){let e=t?.signal??new AbortController().signal,n=w(r,o,i,C(s,i,r.pathname,l),()=>Promise.resolve());try{l=await this.#E(s,n,e)}catch(e){u=e,d=`error`}}return S({error:u,location:r,matches:C(s,i,r.pathname,l),status:d})}async preload(e){this.#b();let t=this.#N(k(e,this.#a,this.#e)),n=this.#_.get(t);if(n)return n;let r=this.load(t,{signal:this.#p.signal}).then(e=>{if(this.#f)throw this.#p.signal.reason;if(e?.status===`error`)throw e.error;if(e){let t=this.#F(e.location);this.#g.size>=20&&this.#g.delete(this.#g.keys().next().value),this.#g.set(t,e.matches.map(e=>e.data))}return e}).catch(e=>{throw this.#o&&this.#C(e,{source:`preload`}),e}).finally(()=>{this.#_.delete(t)});return this.#_.set(t,r),r}waitFor(e){return this.#b(),new Promise((t,n)=>{let r=t=>t.status===`idle`&&t.matches.some(t=>t.name===e);if(this.#d.status===`error`){n(this.#d.error);return}if(r(this.#d)){t(this.#d);return}let i=this.subscribe(e=>{r(e)?(i(),t(e)):e.status===`error`&&(i(),n(e.error))});this.#p.signal.addEventListener(`abort`,()=>{i(),n(this.#p.signal.reason)},{once:!0})})}beforeLeave(e,t){this.#b();let n={handler:e,routes:t?.routes};return this.#l.add(n),()=>{this.#l.delete(n)}}get disposalSignal(){return this.#p.signal}get disposed(){return this.#f}dispose(){this.#f||(this.#f=!0,this.#l.clear(),this.#h.clear(),this.#g.clear(),this.#_.clear(),this.#u.invalidate(new t),this.#y(),this.#p.abort(new t))}[Symbol.dispose](){this.dispose()}#b(){if(this.#f)throw new t}#x(){return this.#r.onPopstate(()=>{let{hash:e,pathname:t,search:n}=this.#r.location,r=`${t}${n}${e}`,i=this.#m;r!==i&&this.#w(this.#S(r,i),{source:`history-listener`})})}async#S(e,t){let n=this.#u.begin(),r=this.#d.matches.map(e=>e.name),i=this.#I(e),a=await D(this.#l,r,i);if(!n.isCurrent())return;if(!a){this.#r.replace(t,this.#d.location.historyState);return}let o=await this.#A(n,h(this.#e,this.#r),(e,t)=>{if(!n.isCurrent())return;let r=this.#F(e);t&&this.#r.replace(r,e.historyState),this.#m=r},void 0,0,!1);n.isCurrent()&&!o&&this.#r.replace(t,this.#d.location.historyState)}#C(e,t){E(e,t,this.#o)}#w(e,t){e.catch(e=>{M(e)?this.#C(e.error,e.context):this.#C(e,t)})}#T(){this.#h.forEach(e=>{e(this.#d)})}async#E(e,t,n){return Promise.all(e.map(async e=>{if(!e.dataFn)return;let r=e.dataFn;try{return await r({...t,signal:n})}catch(r){if(e.onError)return e.onError(r,{...t,signal:n});throw r}}))}async#D(e){let t=e;for(let e=0;e<5;e+=1){let e=new URL(t,`http://localhost`),n={hash:e.hash.replace(/^#/,``),historyState:null,pathname:m(e.pathname,this.#e),query:f(e.search)},r=await this.#O(n);if(r.type!==`redirect`)return r;t=this.#N(r.redirectTo)}throw new r}async#O(e){let{params:t,record:n}=u(e.pathname,this.#i);if(!n)return{location:e,params:t,type:`unmatched`};if(n.redirect)return{location:e,params:t,redirectTo:k(n.redirect,this.#a,this.#e),type:`redirect`};let r=e.query,i=n.coerceSearch??this.#n;if(i)try{r=i(e.query)}catch(t){this.#C(t,{source:`coerce-search`}),r=e.query}return{branch:C(n.branchDefs,t,e.pathname,n.branchDefs.map(()=>void 0)),location:e,params:t,record:n,resolvedQuery:r,type:`matched`}}async#k(e,t,n,r,i,a,o){if(!a())return;let s=e.branchDefs,c=s.some(e=>e.dataFn!=null),l=s.map(()=>void 0);if(c){let e=this.#F(n),c=this.#_.get(e);if(c&&(await c.catch(()=>void 0),!a()))return;let u=this.#g.get(e);if(u)this.#g.delete(e),l=u;else{this.#d=S({location:n,matches:C(s,r,n.pathname,l),status:`loading`}),this.#T();try{l=await this.#E(s,t,i)}catch(e){if(!a())return;throw this.#d=S({error:e,location:n,matches:C(s,r,n.pathname,l),status:`error`}),o?.(e),e}}}a()&&(this.#d=S({location:n,matches:C(s,r,n.pathname,l),status:`idle`}))}async#A(e,t,n,i,a=0,o=!1){let s=this.#d,c=()=>e.isCurrent()&&!this.#f,l=await this.#O(t);if(!c())return!1;if(l.type===`redirect`){if(a>=5)throw new r;return this.#A(e,this.#P(l.redirectTo,t.historyState),n,i,a+1,!0)}if(l.type===`unmatched`){if(this.#v){let r=C([this.#v.leaf],{},t.pathname,[void 0]),a=!1,l,u=!1,d=!1,f=t.query;if(this.#n)try{f=this.#n(t.query)}catch(e){this.#C(e,{source:`coerce-search`})}let p=async()=>{if(!c())return;let i=w(t,f,{},r,(e,t)=>this.navigate(e,t));d=await T(i,[...this.#t,...this.#v.ownMiddleware],async()=>{c()&&(a=!0,n(t,o),c()&&await this.#k(this.#v,i,t,{},e.signal,c,e=>{l=e,u=!0}))},e=>M(e)||u&&e===l?e:j(e,{routeName:`__notFound__`,source:`middleware`}))};try{await this.#j(p,i)}catch(e){throw u&&e===l?j(e,{routeName:`__notFound__`,source:`data-loader`}):e}finally{c()&&a&&(this.#T(),this.#M(this.#d,s))}return d&&a}return!c()||(n(l.location,o),!c())?!1:(this.#d=S({location:l.location,matches:[],status:`idle`}),this.#T(),this.#M(this.#d,s),!0)}let{branch:u,location:d,params:f,record:p,resolvedQuery:m}=l,h=!1,g,_=!1,v=!1,y=async()=>{if(!c())return;let t=w(d,m,f,u,(e,t)=>this.navigate(e,t));v=await T(t,[...this.#t,...p.ownMiddleware],async()=>{c()&&(h=!0,n(d,o),c()&&await this.#k(p,t,d,f,e.signal,c,e=>{g=e,_=!0}))},e=>M(e)||_&&e===g?e:j(e,{routeName:p.leaf.name,source:`middleware`}))};try{await this.#j(y,i)}catch(e){throw _&&e===g?j(e,{routeName:p.leaf.name,source:`data-loader`}):e}finally{c()&&h&&(this.#T(),this.#M(this.#d,s))}return v&&h}async#j(e,t){if((t??this.#c)&&typeof document<`u`){let t=document.startViewTransition;if(t){await t.call(document,e).finished;return}}await e()}#M(e,t){if(!this.#s||typeof window>`u`)return;let n=this.#s(e,t);if(n!==`preserve`){if(n===`top`){window.scrollTo(0,0);return}window.scrollTo(n.x,n.y)}}#N(e){let t=new URL(e,`http://localhost`),n=m(t.pathname,this.#e);return`${o(this.#e,n)}${t.search}${t.hash}`}#P(e,t){let n=new URL(this.#N(e),`http://localhost`);return{hash:n.hash.replace(/^#/,``),historyState:t,pathname:m(n.pathname,this.#e),query:f(n.search)}}#F(e){let t=new URLSearchParams;for(let[n,r]of Object.entries(e.query))Array.isArray(r)?r.forEach(e=>{t.append(n,e)}):t.set(n,r);let n=t.toString(),r=e.hash?`#${e.hash}`:``;return`${o(this.#e,e.pathname)}${n?`?${n}`:``}${r}`}#I(e){let t=new URL(e,`http://localhost`),n=m(t.pathname,this.#e),{params:r,record:i}=u(n,this.#i);return{name:i?.leaf.name,params:r,pathname:n,query:f(t.search)}}async#L(e,t={}){this.#b();let n=this.#N(e);if(!t.force&&n===this.#m)return;let r=this.#u.begin(),i=this.#d.matches.map(e=>e.name),a=await D(this.#l,i,this.#I(n));if(r.isCurrent()&&a)try{await this.#A(r,this.#P(n,t.state),(e,t)=>{if(!r.isCurrent())return;let n=this.#F(e);t?this.#r.replace(n,e.historyState):this.#r.push(n,e.historyState),this.#m=n},t.viewTransition,0,t.replace??!1)}catch(e){throw M(e)?e.error:e}}};function F(e){return new P(e)}export{i as WayfinderApiError,t as WayfinderDisposedError,e as WayfinderError,r as WayfinderRedirectLoopError,n as WayfinderRouteError,g as createBrowserHistory,_ as createHashHistory,v as createMemoryHistory,F as createRouter,y as redirectTo};
|
|
2
2
|
//# sourceMappingURL=wayfinder.js.map
|