@vielzeug/wayfinder 2.0.0 → 2.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/README.md +2 -2
- package/dist/_dev.cjs.map +1 -1
- package/dist/_dev.js.map +1 -1
- package/dist/compile.cjs.map +1 -1
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js.map +1 -1
- package/dist/context.cjs +1 -1
- package/dist/context.cjs.map +1 -1
- package/dist/context.d.ts +4 -4
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +1 -1
- package/dist/context.js.map +1 -1
- package/dist/devtools.cjs.map +1 -1
- package/dist/devtools.d.ts.map +1 -1
- package/dist/devtools.js.map +1 -1
- package/dist/errors.cjs +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.ts +0 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +1 -4
- package/dist/errors.js.map +1 -1
- package/dist/guards.cjs.map +1 -1
- package/dist/guards.d.ts +1 -1
- package/dist/guards.js.map +1 -1
- package/dist/history.cjs +1 -1
- package/dist/history.cjs.map +1 -1
- package/dist/history.d.ts.map +1 -1
- package/dist/history.js +3 -1
- package/dist/history.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/path.cjs +1 -1
- package/dist/path.cjs.map +1 -1
- package/dist/path.d.ts.map +1 -1
- package/dist/path.js +6 -2
- package/dist/path.js.map +1 -1
- package/dist/router.cjs +1 -1
- package/dist/router.cjs.map +1 -1
- package/dist/router.d.ts +3 -4
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +37 -33
- package/dist/router.js.map +1 -1
- package/dist/types.d.ts +13 -14
- package/dist/types.d.ts.map +1 -1
- 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 +33 -28
package/dist/path.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path.js","names":[],"sources":["../src/path.ts"],"sourcesContent":["import type { QueryParams, ResolvedQueryParams, RouteLocation, RouteMatcher, RouteParams, RouteRecord } from './types';\n\nimport { WayfinderRouteError } from './errors';\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<TMeta = unknown, TComponent = unknown>(\n pathname: string,\n record: RouteRecord<TMeta, TComponent>,\n): 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, index) => {\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<TMeta = unknown, TComponent = unknown>(\n pathname: string,\n records: readonly RouteRecord<TMeta, TComponent>[],\n): { params: RouteParams; record?: RouteRecord<TMeta, TComponent> } {\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<TMeta = unknown, TComponent = unknown>(\n pathname: string,\n record: RouteRecord<TMeta, TComponent>,\n): 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)) value.forEach((item) => search.append(key, String(item)));\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\nexport function buildPreloadKey(base: string, path: string, params: RouteParams, query?: QueryParams): string {\n const url = buildUrl(base, path, params);\n\n if (!query || Object.keys(query).length === 0) return url;\n\n const search = new URLSearchParams();\n\n for (const [key, value] of Object.entries(query)) {\n if (Array.isArray(value)) {\n value.forEach((v) => search.append(key, v));\n } else {\n search.set(key, value);\n }\n }\n\n return `${url}?${search.toString()}`;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"path.js","names":[],"sources":["../src/path.ts"],"sourcesContent":["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<TMeta = unknown, TComponent = unknown>(\n pathname: string,\n record: RouteRecord<TMeta, TComponent>,\n): 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, index) => {\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<TMeta = unknown, TComponent = unknown>(\n pathname: string,\n records: readonly RouteRecord<TMeta, TComponent>[],\n): { params: RouteParams; record?: RouteRecord<TMeta, TComponent> } {\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<TMeta = unknown, TComponent = unknown>(\n pathname: string,\n record: RouteRecord<TMeta, TComponent>,\n): 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\nexport function buildPreloadKey(base: string, path: string, params: RouteParams, query?: QueryParams): string {\n const url = buildUrl(base, path, params);\n\n if (!query || Object.keys(query).length === 0) return url;\n\n const search = new URLSearchParams();\n\n for (const [key, value] of Object.entries(query)) {\n if (Array.isArray(value)) {\n value.forEach((v) => {\n search.append(key, v);\n });\n } else {\n search.set(key, value);\n }\n }\n\n return `${url}?${search.toString()}`;\n}\n"],"mappings":"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"}
|
package/dist/router.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("./_navigation.cjs"),t=require("./errors.cjs"),n=require("./path.cjs"),r=require("./compile.cjs"),i=require("./context.cjs"),a=require("./guards.cjs"),o=require("./history.cjs"),s=require("./hydration.cjs"),c=require("./preload.cjs");function l(e,n){let r=n.get(e);if(r)return r;let i=[...n.keys()].join(`, `);throw new t.WayfinderRouteError(i?`Unknown route name: ${e}. Available routes: ${i}`:`Unknown route name: ${e}`)}function u(e,t,r=`/`){if(`path`in e)return e.path;let i=l(e.name,t),a=n.buildUrl(r,i.path,e.params,e.query);return e.hash?`${a}#${e.hash}`:a}var d=Symbol(`wayfinder.errorContext`);function f(e,t){typeof e==`object`&&e&&(e[d]=t)}function p(e){if(typeof e==`object`&&e)return e[d]}function m(e){return typeof e==`object`&&!!e&&typeof e[Symbol.asyncIterator]==`function`&&typeof e.next==`function`&&typeof e.return==`function`}var h=class{#e;#t;#n;#r;#i;#a;#o;#s;#c;#l=new Set;#u=e.createNavigationCoordinator();#d;#f=!1;#p=new AbortController;#m=`/`;#h=new Set;#g;#_;#v;#y;ready;constructor(e){let t=r.compileRoutes(e);if(this.#e=n.normalizePath(e.base??`/`),this.#t=e.middleware??[],this.#n=e.coerceSearch,this.#r=e.history??o.createBrowserHistory(),this.#s=e.viewTransition??!1,this.#o=e.scroll,this.#c=e.onError,this.#i=t.records,this.#a=t.routesByName,this.#_=s.createHydrationManager(),this.#v=c.createPreloadManager(),this.#d=i.createRouteState({location:{hash:``,historyState:null,pathname:`/`,query:{}},matches:[],status:`idle`}),e.notFound){let t=e.notFound,n={component:t.component,dataFn:t.data,meta:t.meta,name:`__notFound__`};this.#g={branchDefs:[n],leaf:n,matcher:{paramNames:[],pattern:/(?:)/,prefixPattern:/(?:)/},ownMiddleware:t.middleware??[],path:`/*`}}else this.#g=null;this.#y=this.#x();let{hash:a,pathname:l,search:u}=this.#r.location;this.#m=`${l}${u}${a}`;let d=this.#u.begin();this.ready=this.#F(d,n.readLocation(this.#e,this.#r),void 0,0,!1,(e,t)=>{if(!d.isCurrent())return;let n=this.#z(e);t&&this.#r.replace(n,e.historyState),this.#m=n}).then(()=>void 0),this.#w(this.ready,{source:`initial-navigation`}),this.beforeLeave=this.beforeLeave.bind(this),this.dispose=this.dispose.bind(this),this.getSnapshot=this.getSnapshot.bind(this),this.isActive=this.isActive.bind(this),this.loadPath=this.loadPath.bind(this),this.matchPath=this.matchPath.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}subscribe(e){return this.#b(),this.#h.add(e),()=>{this.#h.delete(e)}}navigate(e,t){let n=u(typeof e==`string`?{path:e}:e,this.#a);return this.#V(n,t)}url(e,t,r){let i=l(e,this.#a);return n.buildUrl(this.#e,i.path,t,r)}isActive(e,t={}){let r=l(e,this.#a),i=this.#d.location.pathname;return t.exact??!1?n.matchRouteFor(i,[r]).record!=null:n.matchesPrefix(i,r)}matchPath(e){let t=n.stripBase(n.normalizePath(e),this.#e),{params:r,record:a}=n.matchRouteFor(t,this.#i);if(!a||a.redirect)return null;let o=this.#_.effectiveDefs(a),s=i.buildMatchBranch(o,r,t,o.map(()=>void 0));return s.length?s:null}async loadPath(e,t){let n=await this.#k(e);if(n.type!==`matched`)return null;let{location:r,params:a,record:o,resolvedQuery:s}=n,c=this.#_.effectiveDefs(o),l=c.some(e=>e.dataFn!=null),u=c.map(()=>void 0),d,f=`idle`;if(l){let e=t?.signal??new AbortController().signal,n=i.buildMatchBranch(c,a,r.pathname,u),o=i.createRouteContext(r,s,a,n,()=>Promise.resolve());try{u=await this.#D(c,o,e)}catch(e){d=e,f=`error`}}return i.createRouteState({error:d,location:r,matches:i.buildMatchBranch(c,a,r.pathname,u),status:f})}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})})}async preload(e,t,r){this.#b();let i=l(e,this.#a),a=n.buildPreloadKey(this.#e,i.path,t,r),o=this.#v.getInflight(a);if(o)return o;let s=this.#A(a,this.#p.signal,r).finally(()=>{this.#v.untrack(a)});this.#v.track(a,s);try{return await s}catch(e){throw this.#c&&this.#C(e,{source:`preload`}),e}}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.#u.invalidate(new t.WayfinderDisposedError),this.#y(),this.#p.abort(new t.WayfinderDisposedError))}[Symbol.dispose](){this.dispose()}#b(){if(this.#f)throw new t.WayfinderDisposedError}#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 r=this.#u.begin(),i=this.#d.matches.map(e=>e.name),o=this.#B(e),s=await a.runLeaveBlockers(this.#l,i,o);if(!r.isCurrent())return;if(!s){this.#r.replace(t,this.#d.location.historyState);return}let c=await this.#F(r,n.readLocation(this.#e,this.#r),void 0,0,!1,(e,t)=>{if(!r.isCurrent())return;let n=this.#z(e);t&&this.#r.replace(n,e.historyState),this.#m=n});r.isCurrent()&&!c&&this.#r.replace(t,this.#d.location.historyState)}#C(e,t){i.reportError(e,t,this.#c)}#w(e,t){e.catch(e=>{this.#C(e,p(e)??t)})}#T(){this.#h.forEach(e=>e(this.#d))}async#E(e,t){let n;for(;;){if(t.aborted)return await e.return(void 0).catch(()=>void 0),n;let{done:r,value:i}=await e.next();if(r)return i??n;n=i}}async#D(e,t,n){return Promise.all(e.map(async e=>{if(!e.dataFn)return;let r=e.dataFn,i=r({...t,signal:n});try{return m(i)?await this.#E(i,n):await i}catch(r){if(e.onError)return e.onError(r,{...t,signal:n});throw r}}))}async#O(e,t,n,r,a,o){let s=e.map(e=>{if(!e.dataFn)return;let r=e.dataFn;return r({...t,signal:n})}),c=[],l=await Promise.all(s.map(async(r,i)=>{if(m(r)){c.push(i);return}if(r!==void 0)try{return await r}catch(r){let a=e[i];if(a.onError)return a.onError(r,{...t,signal:n});throw r}}));if(c.length===0)return l;let u=[...l],d=(t,n)=>{if(!r())return;u[n]=t;let s=e.map((e,t)=>c.includes(t)?`streaming`:`idle`);this.#d=i.createRouteState({location:a,matches:i.buildMatchBranch(e,o,a.pathname,u,s),status:`streaming`}),this.#T()};return await Promise.all(c.map(async i=>{let a=s[i];try{u[i]=await this.#M(a,i,n,r,d)}catch(r){let a=e[i];if(a.onError)u[i]=await a.onError(r,{...t,signal:n});else throw r}})),u}async#k(e){let r=e;for(let e=0;e<5;e+=1){let e=new URL(r,`http://localhost`),t={hash:e.hash.replace(/^#/,``),historyState:null,pathname:n.stripBase(e.pathname,this.#e),query:n.parseQuery(e.search)},i=await this.#j(t);if(i.type!==`redirect`)return i;r=this.#L(i.redirectTo)}throw new t.WayfinderRedirectLoopError}async#A(e,t,r){let a=await this.#k(e);if(a.type!==`matched`)return;let{location:o,params:s,record:c,resolvedQuery:l}=a,u=this.#_.effectiveDefs(c);if(!u.some(e=>e.dataFn!=null))return;let d=i.buildMatchBranch(u,s,o.pathname,u.map(()=>void 0)),f=i.createRouteContext(o,l,s,d,()=>Promise.resolve()),p=await this.#D(u,f,t);this.#v.set(n.buildPreloadKey(this.#e,c.path,s,r??o.query),p)}async#j(e){let{params:t,record:r}=n.matchRouteFor(e.pathname,this.#i);if(!r)return{location:e,params:t,type:`unmatched`};if(r.redirect)return{location:e,params:t,redirectTo:u(r.redirect,this.#a,this.#e),type:`redirect`};let a=e.query,o=r.coerceSearch??this.#n;if(o)try{a=o(e.query)}catch(t){this.#C(t,{source:`coerce-search`}),a=e.query}await this.#_.hydrate(r);let s=this.#_.effectiveDefs(r);return{branch:i.buildMatchBranch(s,t,e.pathname,s.map(()=>void 0)),location:e,params:t,record:r,resolvedQuery:a,type:`matched`}}async#M(e,t,n,r,i){let a;try{for(;;){if(n.aborted||!r())return await e.return(void 0),a;let{done:o,value:s}=await e.next();if(o)return s??a;a=s,i(s,t)}}catch(t){throw await e.return(void 0).catch(()=>void 0),t}}async#N(e,t,r,a,o,s,c){if(!c())return;let l=this.#_.effectiveDefs(e),u=l.some(e=>e.dataFn!=null),d=l.map(()=>void 0);if(u){let o=n.buildPreloadKey(this.#e,e.path,a,r.query),u=this.#v.consume(o);if(u)d=u;else{let n=l.map(e=>e.dataFn?`loading`:`idle`);this.#d=i.createRouteState({location:r,matches:i.buildMatchBranch(l,a,r.pathname,d,n),status:`loading`}),this.#T();try{d=await this.#O(l,t,s,c,r,a)}catch(t){if(!c())return;throw this.#d=i.createRouteState({error:t,location:r,matches:i.buildMatchBranch(l,a,r.pathname,d),status:`error`}),f(t,{routeName:e.leaf.name,source:`data-loader`}),t}}}c()&&(this.#d=i.createRouteState({location:r,matches:i.buildMatchBranch(l,a,r.pathname,d),status:`idle`}))}async#P(e,t){if((t??this.#s)&&typeof document<`u`){let t=document;if(t.startViewTransition){await t.startViewTransition(e).finished;return}}await e()}async#F(e,n,r,a=0,o=!1,s=()=>void 0){let c=this.#d,l=()=>e.isCurrent()&&!this.#f,u=await this.#j(n);if(!l())return!1;if(u.type===`redirect`){if(a>=5)throw new t.WayfinderRedirectLoopError;return this.#F(e,this.#R(u.redirectTo,n.historyState),r,a+1,!0,s)}if(u.type===`unmatched`){if(this.#g){let t=[this.#g.leaf],a=i.buildMatchBranch(t,{},n.pathname,[void 0]),u=!1,d=!1,f=n.query;if(this.#n)try{f=this.#n(n.query)}catch(e){this.#C(e,{source:`coerce-search`})}let p=async()=>{if(!l())return;let t=i.createRouteContext(n,f,{},a,(e,t)=>this.navigate(e,t));d=await i.executeMiddlewarePipeline(t,[...this.#t,...this.#g.ownMiddleware],async()=>{l()&&(u=!0,s(n,o),l()&&await this.#N(this.#g,t,n,{},a,e.signal,l))})};try{await this.#P(p,r)}finally{l()&&u&&(this.#T(),this.#I(this.#d,c))}return d&&u}return!l()||(s(u.location,o),!l())?!1:(this.#d=i.createRouteState({location:u.location,matches:[],status:`idle`}),this.#T(),this.#I(this.#d,c),!0)}let{branch:d,location:f,params:p,record:m,resolvedQuery:h}=u,g=!1,_=!1,v=async()=>{if(!l())return;let t=i.createRouteContext(f,h,p,d,(e,t)=>this.navigate(e,t));_=await i.executeMiddlewarePipeline(t,[...this.#t,...m.ownMiddleware],async()=>{l()&&(g=!0,s(f,o),l()&&await this.#N(m,t,f,p,d,e.signal,l))})};try{await this.#P(v,r)}finally{l()&&g&&(this.#T(),this.#I(this.#d,c))}return _&&g}#I(e,t){if(!this.#o||typeof window>`u`)return;let n=this.#o(e,t);if(n!==`preserve`){if(n===`top`){window.scrollTo(0,0);return}window.scrollTo(n.x,n.y)}}#L(e){let t=new URL(e,`http://localhost`),r=n.stripBase(t.pathname,this.#e);return`${n.joinPaths(this.#e,r)}${t.search}${t.hash}`}#R(e,t){let r=new URL(this.#L(e),`http://localhost`);return{hash:r.hash.replace(/^#/,``),historyState:t,pathname:n.stripBase(r.pathname,this.#e),query:n.parseQuery(r.search)}}#z(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 r=t.toString(),i=e.hash?`#${e.hash}`:``;return`${n.joinPaths(this.#e,e.pathname)}${r?`?${r}`:``}${i}`}#B(e){let t=new URL(e,`http://localhost`),r=n.stripBase(t.pathname,this.#e),{params:i,record:a}=n.matchRouteFor(r,this.#i);return{name:a?.leaf.name,params:i,pathname:r,query:n.parseQuery(t.search)}}async#V(e,t={}){this.#b();let n=this.#L(e);if(!t.force&&n===this.#m)return;let r=this.#u.begin(),i=this.#d.matches.map(e=>e.name),o=await a.runLeaveBlockers(this.#l,i,this.#B(n));!r.isCurrent()||!o||await this.#F(r,this.#R(n,t.state),t.viewTransition,0,t.replace??!1,(e,t)=>{if(!r.isCurrent())return;let n=this.#z(e);t?this.#r.replace(n,e.historyState):this.#r.push(n,e.historyState),this.#m=n})}};function g(e){return new h(e)}exports.createRouter=g;
|
|
1
|
+
const e=require("./_navigation.cjs"),t=require("./errors.cjs"),n=require("./path.cjs"),r=require("./compile.cjs"),i=require("./context.cjs"),a=require("./guards.cjs"),o=require("./history.cjs"),s=require("./hydration.cjs"),c=require("./preload.cjs");function l(e,n){let r=n.get(e);if(r)return r;let i=[...n.keys()].join(`, `);throw new t.WayfinderRouteError(i?`Unknown route name: ${e}. Available routes: ${i}`:`Unknown route name: ${e}`)}function u(e,t,r=`/`){if(`path`in e)return e.path;let i=l(e.name,t),a=n.buildUrl(r,i.path,e.params,e.query);return e.hash?`${a}#${e.hash}`:a}var d=Symbol(`wayfinder.errorContext`);function f(e,t){typeof e==`object`&&e&&(e[d]=t)}function p(e){if(typeof e==`object`&&e)return e[d]}function m(e){return typeof e==`object`&&!!e&&typeof e[Symbol.asyncIterator]==`function`&&typeof e.next==`function`&&typeof e.return==`function`}var h=class{#e;#t;#n;#r;#i;#a;#o;#s;#c;#l=new Set;#u=e.createNavigationCoordinator();#d;#f=!1;#p=new AbortController;#m=`/`;#h=new Set;#g;#_;#v;#y;ready;constructor(e){let t=r.compileRoutes(e);if(this.#e=n.normalizePath(e.base??`/`),this.#t=e.middleware??[],this.#n=e.coerceSearch,this.#r=e.history??o.createBrowserHistory(),this.#s=e.viewTransition??!1,this.#o=e.scroll,this.#c=e.onError,this.#i=t.records,this.#a=t.routesByName,this.#_=s.createHydrationManager(),this.#v=c.createPreloadManager(),this.#d=i.createRouteState({location:{hash:``,historyState:null,pathname:`/`,query:{}},matches:[],status:`idle`}),e.notFound){let t=e.notFound,n={component:t.component,dataFn:t.data,meta:t.meta,name:`__notFound__`};this.#g={branchDefs:[n],leaf:n,matcher:{paramNames:[],pattern:/(?:)/,prefixPattern:/(?:)/},ownMiddleware:t.middleware??[],path:`/*`}}else this.#g=null;this.#y=this.#x();let{hash:a,pathname:l,search:u}=this.#r.location;this.#m=`${l}${u}${a}`;let d=this.#u.begin();this.ready=this.#F(d,n.readLocation(this.#e,this.#r),(e,t)=>{if(!d.isCurrent())return;let n=this.#z(e);t&&this.#r.replace(n,e.historyState),this.#m=n},void 0,0,!1).then(()=>void 0),this.#w(this.ready,{source:`initial-navigation`}),this.beforeLeave=this.beforeLeave.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}subscribe(e){return this.#b(),this.#h.add(e),()=>{this.#h.delete(e)}}navigate(e,t){let n=u(typeof e==`string`?{path:e}:e,this.#a);return this.#V(n,t)}url(e,t,r){let i=l(e,this.#a);return n.buildUrl(this.#e,i.path,t,r)}isActive(e,t={}){let r=l(e,this.#a),i=this.#d.location.pathname;return t.exact??!1?n.matchRouteFor(i,[r]).record!=null:n.matchesPrefix(i,r)}match(e){let t=n.stripBase(n.normalizePath(e),this.#e),{params:r,record:a}=n.matchRouteFor(t,this.#i);if(!a||a.redirect)return null;let o=this.#_.effectiveDefs(a),s=i.buildMatchBranch(o,r,t,o.map(()=>void 0));return s.length?s:null}async load(e,t){let n=await this.#k(e);if(n.type!==`matched`)return null;let{location:r,params:a,record:o,resolvedQuery:s}=n,c=this.#_.effectiveDefs(o),l=c.some(e=>e.dataFn!=null),u=c.map(()=>void 0),d,f=`idle`;if(l){let e=t?.signal??new AbortController().signal,n=i.buildMatchBranch(c,a,r.pathname,u),o=i.createRouteContext(r,s,a,n,()=>Promise.resolve());try{u=await this.#D(c,o,e)}catch(e){d=e,f=`error`}}return i.createRouteState({error:d,location:r,matches:i.buildMatchBranch(c,a,r.pathname,u),status:f})}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})})}async preload(e,t,r){this.#b();let i=l(e,this.#a),a=n.buildPreloadKey(this.#e,i.path,t,r),o=this.#v.getInflight(a);if(o)return o;let s=this.#A(a,this.#p.signal,r).finally(()=>{this.#v.untrack(a)});this.#v.track(a,s);try{return await s}catch(e){throw this.#c&&this.#C(e,{source:`preload`}),e}}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.#u.invalidate(new t.WayfinderDisposedError),this.#y(),this.#p.abort(new t.WayfinderDisposedError))}[Symbol.dispose](){this.dispose()}#b(){if(this.#f)throw new t.WayfinderDisposedError}#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 r=this.#u.begin(),i=this.#d.matches.map(e=>e.name),o=this.#B(e),s=await a.runLeaveBlockers(this.#l,i,o);if(!r.isCurrent())return;if(!s){this.#r.replace(t,this.#d.location.historyState);return}let c=await this.#F(r,n.readLocation(this.#e,this.#r),(e,t)=>{if(!r.isCurrent())return;let n=this.#z(e);t&&this.#r.replace(n,e.historyState),this.#m=n},void 0,0,!1);r.isCurrent()&&!c&&this.#r.replace(t,this.#d.location.historyState)}#C(e,t){i.reportError(e,t,this.#c)}#w(e,t){e.catch(e=>{this.#C(e,p(e)??t)})}#T(){this.#h.forEach(e=>{e(this.#d)})}async#E(e,t){let n;for(;;){if(t.aborted)return await e.return(void 0).catch(()=>void 0),n;let{done:r,value:i}=await e.next();if(r)return i??n;n=i}}async#D(e,t,n){return Promise.all(e.map(async e=>{if(!e.dataFn)return;let r=e.dataFn,i=r({...t,signal:n});try{return m(i)?await this.#E(i,n):await i}catch(r){if(e.onError)return e.onError(r,{...t,signal:n});throw r}}))}async#O(e,t,n,r,a,o){let s=e.map(e=>{if(!e.dataFn)return;let r=e.dataFn;return r({...t,signal:n})}),c=[],l=await Promise.all(s.map(async(r,i)=>{if(m(r)){c.push(i);return}if(r!==void 0)try{return await r}catch(r){let a=e[i];if(a.onError)return a.onError(r,{...t,signal:n});throw r}}));if(c.length===0)return l;let u=[...l],d=(t,n)=>{if(!r())return;u[n]=t;let s=e.map((e,t)=>c.includes(t)?`streaming`:`idle`);this.#d=i.createRouteState({location:a,matches:i.buildMatchBranch(e,o,a.pathname,u,s),status:`streaming`}),this.#T()};return await Promise.all(c.map(async i=>{let a=s[i];try{u[i]=await this.#M(a,i,n,r,d)}catch(r){let a=e[i];if(a.onError)u[i]=await a.onError(r,{...t,signal:n});else throw r}})),u}async#k(e){let r=e;for(let e=0;e<5;e+=1){let e=new URL(r,`http://localhost`),t={hash:e.hash.replace(/^#/,``),historyState:null,pathname:n.stripBase(e.pathname,this.#e),query:n.parseQuery(e.search)},i=await this.#j(t);if(i.type!==`redirect`)return i;r=this.#L(i.redirectTo)}throw new t.WayfinderRedirectLoopError}async#A(e,t,r){let a=await this.#k(e);if(a.type!==`matched`)return;let{location:o,params:s,record:c,resolvedQuery:l}=a,u=this.#_.effectiveDefs(c);if(!u.some(e=>e.dataFn!=null))return;let d=i.buildMatchBranch(u,s,o.pathname,u.map(()=>void 0)),f=i.createRouteContext(o,l,s,d,()=>Promise.resolve()),p=await this.#D(u,f,t);this.#v.set(n.buildPreloadKey(this.#e,c.path,s,r??o.query),p)}async#j(e){let{params:t,record:r}=n.matchRouteFor(e.pathname,this.#i);if(!r)return{location:e,params:t,type:`unmatched`};if(r.redirect)return{location:e,params:t,redirectTo:u(r.redirect,this.#a,this.#e),type:`redirect`};let a=e.query,o=r.coerceSearch??this.#n;if(o)try{a=o(e.query)}catch(t){this.#C(t,{source:`coerce-search`}),a=e.query}await this.#_.hydrate(r);let s=this.#_.effectiveDefs(r);return{branch:i.buildMatchBranch(s,t,e.pathname,s.map(()=>void 0)),location:e,params:t,record:r,resolvedQuery:a,type:`matched`}}async#M(e,t,n,r,i){let a;try{for(;;){if(n.aborted||!r())return await e.return(void 0),a;let{done:o,value:s}=await e.next();if(o)return s??a;a=s,i(s,t)}}catch(t){throw await e.return(void 0).catch(()=>void 0),t}}async#N(e,t,r,a,o,s){if(!s())return;let c=this.#_.effectiveDefs(e),l=c.some(e=>e.dataFn!=null),u=c.map(()=>void 0);if(l){let l=n.buildPreloadKey(this.#e,e.path,a,r.query),d=this.#v.consume(l);if(d)u=d;else{let n=c.map(e=>e.dataFn?`loading`:`idle`);this.#d=i.createRouteState({location:r,matches:i.buildMatchBranch(c,a,r.pathname,u,n),status:`loading`}),this.#T();try{u=await this.#O(c,t,o,s,r,a)}catch(t){if(!s())return;throw this.#d=i.createRouteState({error:t,location:r,matches:i.buildMatchBranch(c,a,r.pathname,u),status:`error`}),f(t,{routeName:e.leaf.name,source:`data-loader`}),t}}}s()&&(this.#d=i.createRouteState({location:r,matches:i.buildMatchBranch(c,a,r.pathname,u),status:`idle`}))}async#P(e,t){if((t??this.#s)&&typeof document<`u`){let t=document;if(t.startViewTransition){await t.startViewTransition(e).finished;return}}await e()}async#F(e,n,r,a,o=0,s=!1){let c=this.#d,l=()=>e.isCurrent()&&!this.#f,u=await this.#j(n);if(!l())return!1;if(u.type===`redirect`){if(o>=5)throw new t.WayfinderRedirectLoopError;return this.#F(e,this.#R(u.redirectTo,n.historyState),r,a,o+1,!0)}if(u.type===`unmatched`){if(this.#g){let t=[this.#g.leaf],o=i.buildMatchBranch(t,{},n.pathname,[void 0]),u=!1,d=!1,f=n.query;if(this.#n)try{f=this.#n(n.query)}catch(e){this.#C(e,{source:`coerce-search`})}let p=async()=>{if(!l())return;let t=i.createRouteContext(n,f,{},o,(e,t)=>this.navigate(e,t));d=await i.executeMiddlewarePipeline(t,[...this.#t,...this.#g.ownMiddleware],async()=>{l()&&(u=!0,r(n,s),l()&&await this.#N(this.#g,t,n,{},e.signal,l))})};try{await this.#P(p,a)}finally{l()&&u&&(this.#T(),this.#I(this.#d,c))}return d&&u}return!l()||(r(u.location,s),!l())?!1:(this.#d=i.createRouteState({location:u.location,matches:[],status:`idle`}),this.#T(),this.#I(this.#d,c),!0)}let{branch:d,location:f,params:p,record:m,resolvedQuery:h}=u,g=!1,_=!1,v=async()=>{if(!l())return;let t=i.createRouteContext(f,h,p,d,(e,t)=>this.navigate(e,t));_=await i.executeMiddlewarePipeline(t,[...this.#t,...m.ownMiddleware],async()=>{l()&&(g=!0,r(f,s),l()&&await this.#N(m,t,f,p,e.signal,l))})};try{await this.#P(v,a)}finally{l()&&g&&(this.#T(),this.#I(this.#d,c))}return _&&g}#I(e,t){if(!this.#o||typeof window>`u`)return;let n=this.#o(e,t);if(n!==`preserve`){if(n===`top`){window.scrollTo(0,0);return}window.scrollTo(n.x,n.y)}}#L(e){let t=new URL(e,`http://localhost`),r=n.stripBase(t.pathname,this.#e);return`${n.joinPaths(this.#e,r)}${t.search}${t.hash}`}#R(e,t){let r=new URL(this.#L(e),`http://localhost`);return{hash:r.hash.replace(/^#/,``),historyState:t,pathname:n.stripBase(r.pathname,this.#e),query:n.parseQuery(r.search)}}#z(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 r=t.toString(),i=e.hash?`#${e.hash}`:``;return`${n.joinPaths(this.#e,e.pathname)}${r?`?${r}`:``}${i}`}#B(e){let t=new URL(e,`http://localhost`),r=n.stripBase(t.pathname,this.#e),{params:i,record:a}=n.matchRouteFor(r,this.#i);return{name:a?.leaf.name,params:i,pathname:r,query:n.parseQuery(t.search)}}async#V(e,t={}){this.#b();let n=this.#L(e);if(!t.force&&n===this.#m)return;let r=this.#u.begin(),i=this.#d.matches.map(e=>e.name),o=await a.runLeaveBlockers(this.#l,i,this.#B(n));!r.isCurrent()||!o||await this.#F(r,this.#R(n,t.state),(e,t)=>{if(!r.isCurrent())return;let n=this.#z(e);t?this.#r.replace(n,e.historyState):this.#r.push(n,e.historyState),this.#m=n},t.viewTransition,0,t.replace??!1)}};function g(e){return new h(e)}exports.createRouter=g;
|
|
2
2
|
//# sourceMappingURL=router.cjs.map
|
package/dist/router.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.cjs","names":["#base","#globalMiddleware","#globalCoerceSearch","#history","#records","#routesByName","#scroll","#useViewTransition","#onError","#beforeLeaveBlockers","#navigation","#disposeController","#listeners","#notFoundRecord","#hydration","#preload","#unlistenHistory","#currentState","#registerHistoryListener","#lastHref","#handleRoute","#hrefForLocation","#runInBackground","#assertNotDisposed","#navigateToPath","#resolveUrl","#loadDataDrain","#doPreload","#reportError","#disposed","#handleHistoryNavigation","#navigationDestination","#drainGenerator","#loadDataStream","#notifyListeners","#runStreamingLoader","#prepareRoute","#resolveDestination","#runTerminal","#runWithTransition","#locationFromPath","#applyScroll"],"sources":["../src/router.ts"],"sourcesContent":["import type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n HistoryDriver,\n IsActiveOptions,\n MatchStatus,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n PathParams,\n QueryParams,\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 Unsubscribe,\n} from './types';\n\nimport { 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 { createHydrationManager } from './hydration';\nimport {\n buildPreloadKey,\n buildUrl,\n joinPaths,\n matchesPrefix,\n matchRouteFor,\n normalizePath,\n parseQuery,\n readLocation,\n stripBase,\n} from './path';\nimport { createPreloadManager } from './preload';\n\n// ─── Module-level helpers (formerly in resolve.ts) ────────────────────────────\n\nfunction getRouteByName<TMeta, TComponent>(\n name: string,\n routesByName: ReadonlyMap<string, RouteRecord<TMeta, TComponent>>,\n): RouteRecord<TMeta, TComponent> {\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<TMeta, TComponent>(\n target: { path: string } | { hash?: string; name: string; params?: RouteParams; query?: ResolvedQueryParams },\n routesByName: ReadonlyMap<string, RouteRecord<TMeta, TComponent>>,\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 helper ─────────────────────────────────────────────────────────\n\nconst ERROR_CONTEXT = Symbol('wayfinder.errorContext');\n\nfunction attachErrorContext(error: unknown, context: RouterErrorContext): void {\n if (error !== null && typeof error === 'object') {\n (error as Record<symbol, RouterErrorContext>)[ERROR_CONTEXT] = context;\n }\n}\n\nfunction getErrorContext(error: unknown): RouterErrorContext | undefined {\n if (error !== null && typeof error === 'object') {\n return (error as Record<symbol, RouterErrorContext | undefined>)[ERROR_CONTEXT];\n }\n\n return undefined;\n}\n\n// ─── Internal types ───────────────────────────────────────────────────────────\n\ntype NavigationDecision<TMeta, TComponent> =\n | {\n branch: RouteMatchBranch<TMeta, TComponent>;\n location: RouteLocation;\n params: RouteParams;\n record: RouteRecord<TMeta, TComponent>;\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// ─── Streaming helper ─────────────────────────────────────────────────────────\n\nfunction isAsyncGenerator(value: unknown): value is AsyncGenerator<unknown, unknown> {\n return (\n value !== null &&\n typeof value === 'object' &&\n typeof (value as AsyncGenerator)[Symbol.asyncIterator] === 'function' &&\n typeof (value as AsyncGenerator).next === 'function' &&\n typeof (value as AsyncGenerator).return === 'function'\n );\n}\n\n// ─── Router class ─────────────────────────────────────────────────────────────\n\nclass Router<TRoutes extends RouteTable, TMeta = unknown, TComponent = unknown> {\n readonly #base: string;\n readonly #globalMiddleware: readonly Middleware[];\n readonly #globalCoerceSearch?: CoerceSearchFn;\n readonly #history: HistoryDriver;\n readonly #records: readonly RouteRecord<TMeta, TComponent>[];\n readonly #routesByName: ReadonlyMap<string, RouteRecord<TMeta, TComponent>>;\n readonly #scroll?: RouterOptions<TRoutes, TMeta, TComponent>['scroll'];\n readonly #useViewTransition: boolean;\n readonly #onError?: RouterOptions<TRoutes, TMeta, TComponent>['onError'];\n\n // Mutable navigation state\n readonly #beforeLeaveBlockers = new Set<RegisteredBlocker>();\n readonly #navigation = createNavigationCoordinator();\n #currentState: RouteState<TMeta, TComponent>;\n #disposed = false;\n readonly #disposeController = new AbortController();\n #lastHref = '/';\n readonly #listeners = new Set<(state: RouteState<TMeta, TComponent>) => void>();\n // F5: compiled notFound fallback record\n readonly #notFoundRecord: RouteRecord<TMeta, TComponent> | null;\n\n // Sub-managers\n readonly #hydration: ReturnType<typeof createHydrationManager<TMeta, TComponent>>;\n readonly #preload: ReturnType<typeof createPreloadManager>;\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, TMeta, TComponent>) {\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.#useViewTransition = options.viewTransition ?? false;\n this.#scroll = options.scroll;\n this.#onError = options.onError;\n this.#records = compiled.records as unknown as readonly RouteRecord<TMeta, TComponent>[];\n this.#routesByName = compiled.routesByName as unknown as ReadonlyMap<string, RouteRecord<TMeta, TComponent>>;\n this.#hydration = createHydrationManager<TMeta, TComponent>();\n this.#preload = createPreloadManager();\n this.#currentState = createRouteState<TMeta, TComponent>({\n location: { hash: '', historyState: null, pathname: '/', query: {} },\n matches: [] as RouteMatchBranch<TMeta, TComponent>,\n status: 'idle',\n });\n\n // F5: Build a synthetic RouteRecord for the notFound fallback.\n if (options.notFound) {\n const nf = options.notFound;\n const leafDef: RouteBranchDef<TMeta, TComponent> = {\n component: nf.component as TComponent | undefined,\n dataFn: nf.data,\n meta: nf.meta as TMeta | undefined,\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 this.ready = this.#handleRoute(\n attempt,\n readLocation(this.#base, this.#history),\n undefined,\n 0,\n false,\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 ).then(() => undefined);\n this.#runInBackground(this.ready, { 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.dispose = this.dispose.bind(this);\n this.getSnapshot = this.getSnapshot.bind(this);\n this.isActive = this.isActive.bind(this);\n this.loadPath = this.loadPath.bind(this);\n this.matchPath = this.matchPath.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<TMeta, TComponent> {\n return this.#currentState;\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<TMeta, TComponent>) => 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 matchPath(pathname: string): RouteMatchBranch<TMeta, TComponent> | 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 defs = this.#hydration.effectiveDefs(record);\n const branch = buildMatchBranch(\n defs,\n params,\n normalizedPathname,\n defs.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 and may resolve lazy modules.\n *\n * R5: Accepts an options object instead of a bare AbortSignal.\n */\n async loadPath(url: string, options?: { signal?: AbortSignal }): Promise<RouteState<TMeta, TComponent> | 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 = this.#hydration.effectiveDefs(record);\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.#loadDataDrain(defs, context, effectiveSignal);\n } catch (e) {\n error = e;\n status = 'error';\n }\n }\n\n return createRouteState<TMeta, TComponent>({\n error,\n location,\n matches: buildMatchBranch(defs, params, location.pathname, dataResults),\n status,\n });\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<TMeta, TComponent>> {\n this.#assertNotDisposed();\n\n return new Promise((resolve, reject) => {\n const matchesName = (state: RouteState<TMeta, TComponent>): 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 // ─── Preload ──────────────────────────────────────────────────────────────\n\n /**\n * Eagerly execute the data loaders for a named route without navigating.\n * Results are cached and reused during the next navigation to the same route.\n * Concurrent calls for the same route are deduplicated.\n *\n * Pass the same `query` you intend to navigate with to ensure the cached result\n * matches the navigation's cache key. Without `query`, the preload key is the\n * bare path — any navigation with a query string will produce a cache miss.\n *\n * @throws {WayfinderDisposedError} if the router has already been disposed.\n */\n async preload<Name extends RouteName<TRoutes>>(\n name: Name,\n params?: PathParams<RoutePathByName<TRoutes, Name>>,\n query?: QueryParams,\n ): Promise<void> {\n this.#assertNotDisposed();\n\n const route = getRouteByName(name, this.#routesByName);\n const cacheKey = buildPreloadKey(this.#base, route.path, params as RouteParams, query);\n\n const inflight = this.#preload.getInflight(cacheKey);\n\n if (inflight) return inflight;\n\n const work = this.#doPreload(cacheKey, this.#disposeController.signal, query).finally(() => {\n this.#preload.untrack(cacheKey);\n });\n\n this.#preload.track(cacheKey, work);\n\n try {\n return await work;\n } catch (error) {\n if (this.#onError) this.#reportError(error, { source: 'preload' });\n\n throw error;\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.#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 undefined,\n 0,\n false,\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 );\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((error) => {\n this.#reportError(error, getErrorContext(error) ?? context);\n });\n }\n\n // ─── Private: listener notification ──────────────────────────────────────\n\n #notifyListeners(): void {\n this.#listeners.forEach((listener) => listener(this.#currentState));\n }\n\n // ─── Private: data loaders ────────────────────────────────────────────────\n\n /**\n * R10: Drain an async generator to completion. Private class method replaces\n * the module-level `drainGenerator` helper.\n */\n async #drainGenerator(gen: AsyncGenerator<unknown, unknown>, signal: AbortSignal): Promise<unknown> {\n let lastYield: unknown;\n\n while (true) {\n if (signal.aborted) {\n await gen.return(undefined as unknown).catch(() => undefined);\n\n return lastYield;\n }\n\n const { done, value } = await gen.next();\n\n if (done) return value ?? lastYield;\n\n lastYield = value;\n }\n }\n\n /**\n * Drain all data loaders to completion. Async generators are consumed entirely.\n * Used in `loadPath()` and `preload()`. Per-def `onError` boundaries are applied.\n */\n async #loadDataDrain(\n defs: readonly RouteBranchDef<TMeta, TComponent>[],\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 const raw = dataFn({ ...context, signal } as DataContext<RouteParams, TRoutes>);\n\n try {\n return isAsyncGenerator(raw) ? await this.#drainGenerator(raw, signal) : await (raw as Promise<unknown>);\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 /**\n * Run all data loaders in streaming mode. Generators yield partial states via `onPartial`.\n * Non-generator loaders are awaited normally. Per-def `onError` boundaries are applied.\n * Used during live navigation in `#runTerminal`.\n */\n async #loadDataStream(\n defs: readonly RouteBranchDef<TMeta, TComponent>[],\n context: RouteContext<RouteParams, TRoutes>,\n signal: AbortSignal,\n isCurrent: () => boolean,\n location: RouteLocation,\n params: RouteParams,\n ): Promise<unknown[]> {\n const rawResults: Array<AsyncGenerator<unknown, unknown> | unknown> = defs.map((def) => {\n if (!def.dataFn) return undefined;\n\n const dataFn = def.dataFn as unknown as DataFn<RouteParams, TRoutes>;\n\n return dataFn({ ...context, signal } as DataContext<RouteParams, TRoutes>);\n });\n\n const streamingIndices: number[] = [];\n\n const settled: unknown[] = await Promise.all(\n rawResults.map(async (raw, i) => {\n if (isAsyncGenerator(raw)) {\n streamingIndices.push(i);\n\n return undefined;\n }\n\n if (raw === undefined) return undefined;\n\n try {\n return await (raw as Promise<unknown>);\n } catch (err) {\n const def = defs[i]!;\n\n if (def.onError) return def.onError(err, { ...context, signal } as unknown as DataContext);\n\n throw err;\n }\n }),\n );\n\n if (streamingIndices.length === 0) return settled;\n\n const streamingData: unknown[] = [...settled];\n\n const onPartial = (value: unknown, idx: number): void => {\n if (!isCurrent()) return;\n\n streamingData[idx] = value;\n\n const nodeStatuses: MatchStatus[] = defs.map((_, i) => (streamingIndices.includes(i) ? 'streaming' : 'idle'));\n\n this.#currentState = createRouteState<TMeta, TComponent>({\n location,\n matches: buildMatchBranch(defs, params, location.pathname, streamingData, nodeStatuses),\n status: 'streaming',\n });\n this.#notifyListeners();\n };\n\n await Promise.all(\n streamingIndices.map(async (idx) => {\n const gen = rawResults[idx] as AsyncGenerator<unknown, unknown>;\n\n try {\n streamingData[idx] = await this.#runStreamingLoader(gen, idx, signal, isCurrent, onPartial);\n } catch (err) {\n const def = defs[idx]!;\n\n if (def.onError) {\n streamingData[idx] = await def.onError(err, { ...context, signal } as unknown as DataContext);\n } else {\n throw err;\n }\n }\n }),\n );\n\n return streamingData;\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<TMeta, TComponent>> {\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: preload ─────────────────────────────────────────────────────\n\n async #doPreload(startUrl: string, signal: AbortSignal, query?: QueryParams): Promise<void> {\n const prepared = await this.#resolveUrl(startUrl);\n\n if (prepared.type !== 'matched') return;\n\n const { location, params, record, resolvedQuery } = prepared;\n const defs = this.#hydration.effectiveDefs(record);\n const hasData = defs.some((d) => d.dataFn != null);\n\n if (!hasData) return;\n\n const branch = buildMatchBranch(\n defs,\n params,\n location.pathname,\n defs.map(() => undefined),\n );\n const context = createRouteContext<TRoutes>(location, resolvedQuery, params, branch, () => Promise.resolve());\n const results = await this.#loadDataDrain(defs, context, signal);\n\n this.#preload.set(buildPreloadKey(this.#base, record.path, params, query ?? location.query), results);\n }\n\n // ─── Private: route preparation ───────────────────────────────────────────\n\n async #prepareRoute(location: RouteLocation): Promise<NavigationDecision<TMeta, TComponent>> {\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 await this.#hydration.hydrate(record);\n\n const defs = this.#hydration.effectiveDefs(record);\n\n return {\n branch: buildMatchBranch(\n defs,\n params,\n location.pathname,\n defs.map(() => undefined),\n ),\n location,\n params,\n record,\n resolvedQuery,\n type: 'matched',\n };\n }\n\n // ─── Private: streaming data loader ──────────────────────────────────────\n\n /**\n * Drain an AsyncGenerator data loader, emitting `status: 'streaming'` for every yielded\n * partial value and returning the generator's return value as the final settled data.\n * Aborts cleanly when the signal fires or the navigation is superseded.\n */\n async #runStreamingLoader(\n generator: AsyncGenerator<unknown, unknown>,\n defIndex: number,\n signal: AbortSignal,\n isCurrent: () => boolean,\n onPartial: (data: unknown, defIndex: number) => void,\n ): Promise<unknown> {\n let lastValue: unknown;\n\n try {\n while (true) {\n if (signal.aborted || !isCurrent()) {\n await generator.return(undefined as unknown);\n\n return lastValue;\n }\n\n const { done, value } = await generator.next();\n\n if (done) return value ?? lastValue;\n\n lastValue = value;\n onPartial(value, defIndex);\n }\n } catch (err) {\n await generator.return(undefined as unknown).catch(() => undefined);\n throw err;\n }\n }\n\n // ─── Private: terminal (data only — F4: handler removed) ─────────────────\n\n async #runTerminal(\n record: RouteRecord<TMeta, TComponent>,\n context: RouteContext<RouteParams, TRoutes>,\n location: RouteLocation,\n params: RouteParams,\n _initialBranch: RouteMatchBranch<TMeta, TComponent>,\n signal: AbortSignal,\n isCurrent: () => boolean,\n ): Promise<void> {\n if (!isCurrent()) return;\n\n const defs = this.#hydration.effectiveDefs(record);\n const hasData = defs.some((d) => d.dataFn != null);\n let dataResults: unknown[] = defs.map(() => undefined);\n\n if (hasData) {\n const preloadKey = buildPreloadKey(this.#base, record.path, params, location.query);\n const cached = this.#preload.consume(preloadKey);\n\n if (cached) {\n dataResults = cached;\n } else {\n // Emit per-node loading state (F1) while data is in-flight.\n const loadingStatuses: MatchStatus[] = defs.map((d) => (d.dataFn ? 'loading' : 'idle'));\n\n this.#currentState = createRouteState<TMeta, TComponent>({\n location,\n matches: buildMatchBranch(defs, params, location.pathname, dataResults, loadingStatuses),\n status: 'loading',\n });\n this.#notifyListeners();\n\n try {\n dataResults = await this.#loadDataStream(defs, context, signal, isCurrent, location, params);\n } catch (error) {\n if (!isCurrent()) return;\n\n this.#currentState = createRouteState<TMeta, TComponent>({\n error,\n location,\n matches: buildMatchBranch(defs, params, location.pathname, dataResults),\n status: 'error',\n });\n // R3: attach enriched context so the eventual reporter uses it.\n attachErrorContext(error, { routeName: record.leaf.name, source: 'data-loader' });\n // Do not call #notifyListeners here — the finally block in #handleRoute does it once.\n throw error;\n }\n }\n }\n\n if (!isCurrent()) return;\n\n this.#currentState = createRouteState<TMeta, TComponent>({\n location,\n matches: buildMatchBranch(defs, params, location.pathname, dataResults),\n status: 'idle',\n });\n }\n\n // ─── Private: view transitions ────────────────────────────────────────────\n\n async #runWithTransition(run: () => Promise<void>, useTransition?: boolean): Promise<void> {\n const shouldUseTransition = useTransition ?? this.#useViewTransition;\n\n if (shouldUseTransition && typeof document !== 'undefined') {\n type ViewTransitionDocument = Document & {\n startViewTransition?: (callback: () => void | Promise<void>) => { finished: Promise<void> };\n };\n\n const doc = document as ViewTransitionDocument;\n\n if (doc.startViewTransition) {\n await doc.startViewTransition(run).finished;\n\n return;\n }\n }\n\n await run();\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 useTransition?: boolean,\n depth = 0,\n replace = false,\n commit: (location: RouteLocation, replace: boolean) => void = () => undefined,\n ): Promise<boolean> {\n const prevState = 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 useTransition,\n depth + 1,\n true,\n commit,\n );\n }\n\n if (prepared.type === 'unmatched') {\n // F5: 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 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 nfBranch,\n attempt.signal,\n isCurrent,\n );\n },\n );\n };\n\n try {\n await this.#runWithTransition(run, useTransition);\n } finally {\n if (isCurrent() && committed) {\n this.#notifyListeners();\n this.#applyScroll(this.#currentState, prevState);\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<TMeta, TComponent>({\n location: prepared.location,\n matches: [] as RouteMatchBranch<TMeta, TComponent>,\n status: 'idle',\n });\n this.#notifyListeners();\n this.#applyScroll(this.#currentState, prevState);\n\n return true;\n }\n\n const { branch, location, params, record, resolvedQuery } = prepared;\n let committed = 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, branch, attempt.signal, isCurrent);\n },\n );\n };\n\n try {\n await this.#runWithTransition(run, useTransition);\n } finally {\n if (isCurrent() && committed) {\n this.#notifyListeners();\n this.#applyScroll(this.#currentState, prevState);\n }\n }\n\n return terminalRan && committed;\n }\n\n // ─── Private: scroll ─────────────────────────────────────────────────────\n\n #applyScroll(to: RouteState<TMeta, TComponent>, from: RouteState<TMeta, TComponent>): 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)) value.forEach((item) => search.append(key, item));\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 await this.#handleRoute(\n attempt,\n this.#locationFromPath(destination, options.state),\n options.viewTransition,\n 0,\n options.replace ?? false,\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 );\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, TMeta = unknown, TComponent = unknown>(\n options: RouterOptions<TRoutes, TMeta, TComponent>,\n): Router<TRoutes, TMeta, TComponent> {\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":"router.cjs","names":["#base","#globalMiddleware","#globalCoerceSearch","#history","#records","#routesByName","#scroll","#useViewTransition","#onError","#beforeLeaveBlockers","#navigation","#disposeController","#listeners","#notFoundRecord","#hydration","#preload","#unlistenHistory","#currentState","#registerHistoryListener","#lastHref","#handleRoute","#hrefForLocation","#runInBackground","#assertNotDisposed","#navigateToPath","#resolveUrl","#loadDataDrain","#doPreload","#reportError","#disposed","#handleHistoryNavigation","#navigationDestination","#drainGenerator","#loadDataStream","#notifyListeners","#runStreamingLoader","#prepareRoute","#resolveDestination","#runTerminal","#runWithTransition","#locationFromPath","#applyScroll"],"sources":["../src/router.ts"],"sourcesContent":["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 { createHydrationManager } from './hydration';\nimport {\n buildPreloadKey,\n buildUrl,\n joinPaths,\n matchesPrefix,\n matchRouteFor,\n normalizePath,\n parseQuery,\n readLocation,\n stripBase,\n} from './path';\nimport { createPreloadManager } from './preload';\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 NavigationStatus,\n PathParams,\n QueryParams,\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 Unsubscribe,\n} from './types';\n\n// ─── Module-level helpers (formerly in resolve.ts) ────────────────────────────\n\nfunction getRouteByName<TMeta, TComponent>(\n name: string,\n routesByName: ReadonlyMap<string, RouteRecord<TMeta, TComponent>>,\n): RouteRecord<TMeta, TComponent> {\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<TMeta, TComponent>(\n target: { path: string } | { hash?: string; name: string; params?: RouteParams; query?: ResolvedQueryParams },\n routesByName: ReadonlyMap<string, RouteRecord<TMeta, TComponent>>,\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 helper ─────────────────────────────────────────────────────────\n\nconst ERROR_CONTEXT = Symbol('wayfinder.errorContext');\n\nfunction attachErrorContext(error: unknown, context: RouterErrorContext): void {\n if (error !== null && typeof error === 'object') {\n (error as Record<symbol, RouterErrorContext>)[ERROR_CONTEXT] = context;\n }\n}\n\nfunction getErrorContext(error: unknown): RouterErrorContext | undefined {\n if (error !== null && typeof error === 'object') {\n return (error as Record<symbol, RouterErrorContext | undefined>)[ERROR_CONTEXT];\n }\n\n return undefined;\n}\n\n// ─── Internal types ───────────────────────────────────────────────────────────\n\ntype NavigationDecision<TMeta, TComponent> =\n | {\n branch: RouteMatchBranch<TMeta, TComponent>;\n location: RouteLocation;\n params: RouteParams;\n record: RouteRecord<TMeta, TComponent>;\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// ─── Streaming helper ─────────────────────────────────────────────────────────\n\nfunction isAsyncGenerator(value: unknown): value is AsyncGenerator<unknown, unknown> {\n return (\n value !== null &&\n typeof value === 'object' &&\n typeof (value as AsyncGenerator)[Symbol.asyncIterator] === 'function' &&\n typeof (value as AsyncGenerator).next === 'function' &&\n typeof (value as AsyncGenerator).return === 'function'\n );\n}\n\n// ─── Router class ─────────────────────────────────────────────────────────────\n\nclass Router<TRoutes extends RouteTable, TMeta = unknown, TComponent = unknown> {\n readonly #base: string;\n readonly #globalMiddleware: readonly Middleware[];\n readonly #globalCoerceSearch?: CoerceSearchFn;\n readonly #history: HistoryDriver;\n readonly #records: readonly RouteRecord<TMeta, TComponent>[];\n readonly #routesByName: ReadonlyMap<string, RouteRecord<TMeta, TComponent>>;\n readonly #scroll?: RouterOptions<TRoutes, TMeta, TComponent>['scroll'];\n readonly #useViewTransition: boolean;\n readonly #onError?: RouterOptions<TRoutes, TMeta, TComponent>['onError'];\n\n // Mutable navigation state\n readonly #beforeLeaveBlockers = new Set<RegisteredBlocker>();\n readonly #navigation = createNavigationCoordinator();\n #currentState: RouteState<TMeta, TComponent>;\n #disposed = false;\n readonly #disposeController = new AbortController();\n #lastHref = '/';\n readonly #listeners = new Set<(state: RouteState<TMeta, TComponent>) => void>();\n // Compiled notFound fallback record\n readonly #notFoundRecord: RouteRecord<TMeta, TComponent> | null;\n\n // Sub-managers\n readonly #hydration: ReturnType<typeof createHydrationManager<TMeta, TComponent>>;\n readonly #preload: ReturnType<typeof createPreloadManager>;\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, TMeta, TComponent>) {\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.#useViewTransition = options.viewTransition ?? false;\n this.#scroll = options.scroll;\n this.#onError = options.onError;\n this.#records = compiled.records as unknown as readonly RouteRecord<TMeta, TComponent>[];\n this.#routesByName = compiled.routesByName as unknown as ReadonlyMap<string, RouteRecord<TMeta, TComponent>>;\n this.#hydration = createHydrationManager<TMeta, TComponent>();\n this.#preload = createPreloadManager();\n this.#currentState = createRouteState<TMeta, TComponent>({\n location: { hash: '', historyState: null, pathname: '/', query: {} },\n matches: [] as RouteMatchBranch<TMeta, TComponent>,\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<TMeta, TComponent> = {\n component: nf.component as TComponent | undefined,\n dataFn: nf.data,\n meta: nf.meta as TMeta | undefined,\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 this.ready = 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 this.#runInBackground(this.ready, { 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.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<TMeta, TComponent> {\n return this.#currentState;\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<TMeta, TComponent>) => 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<TMeta, TComponent> | 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 defs = this.#hydration.effectiveDefs(record);\n const branch = buildMatchBranch(\n defs,\n params,\n normalizedPathname,\n defs.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 and may resolve lazy modules.\n * Middleware is not executed — use `navigate()` when middleware side effects are needed.\n */\n async load(url: string, options?: { signal?: AbortSignal }): Promise<RouteState<TMeta, TComponent> | 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 = this.#hydration.effectiveDefs(record);\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.#loadDataDrain(defs, context, effectiveSignal);\n } catch (e) {\n error = e;\n status = 'error';\n }\n }\n\n return createRouteState<TMeta, TComponent>({\n error,\n location,\n matches: buildMatchBranch(defs, params, location.pathname, dataResults),\n status,\n });\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<TMeta, TComponent>> {\n this.#assertNotDisposed();\n\n return new Promise((resolve, reject) => {\n const matchesName = (state: RouteState<TMeta, TComponent>): 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 // ─── Preload ──────────────────────────────────────────────────────────────\n\n /**\n * Eagerly execute the data loaders for a named route without navigating.\n * Results are cached and reused during the next navigation to the same route.\n * Concurrent calls for the same route are deduplicated.\n *\n * Pass the same `query` you intend to navigate with to ensure the cached result\n * matches the navigation's cache key. Without `query`, the preload key is the\n * bare path — any navigation with a query string will produce a cache miss.\n *\n * @throws {WayfinderDisposedError} if the router has already been disposed.\n */\n async preload<Name extends RouteName<TRoutes>>(\n name: Name,\n params?: PathParams<RoutePathByName<TRoutes, Name>>,\n query?: QueryParams,\n ): Promise<void> {\n this.#assertNotDisposed();\n\n const route = getRouteByName(name, this.#routesByName);\n const cacheKey = buildPreloadKey(this.#base, route.path, params as RouteParams, query);\n\n const inflight = this.#preload.getInflight(cacheKey);\n\n if (inflight) return inflight;\n\n const work = this.#doPreload(cacheKey, this.#disposeController.signal, query).finally(() => {\n this.#preload.untrack(cacheKey);\n });\n\n this.#preload.track(cacheKey, work);\n\n try {\n return await work;\n } catch (error) {\n if (this.#onError) this.#reportError(error, { source: 'preload' });\n\n throw error;\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.#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((error) => {\n this.#reportError(error, getErrorContext(error) ?? context);\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 * R10: Drain an async generator to completion. Private class method replaces\n * the module-level `drainGenerator` helper.\n */\n async #drainGenerator(gen: AsyncGenerator<unknown, unknown>, signal: AbortSignal): Promise<unknown> {\n let lastYield: unknown;\n\n while (true) {\n if (signal.aborted) {\n await gen.return(undefined as unknown).catch(() => undefined);\n\n return lastYield;\n }\n\n const { done, value } = await gen.next();\n\n if (done) return value ?? lastYield;\n\n lastYield = value;\n }\n }\n\n /**\n * Drain all data loaders to completion. Async generators are consumed entirely.\n * Used in `load()` and `preload()`. Per-def `onError` boundaries are applied.\n */\n async #loadDataDrain(\n defs: readonly RouteBranchDef<TMeta, TComponent>[],\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 const raw = dataFn({ ...context, signal } as DataContext<RouteParams, TRoutes>);\n\n try {\n return isAsyncGenerator(raw) ? await this.#drainGenerator(raw, signal) : await (raw as Promise<unknown>);\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 /**\n * Run all data loaders in streaming mode. Generators yield partial states via `onPartial`.\n * Non-generator loaders are awaited normally. Per-def `onError` boundaries are applied.\n * Used during live navigation in `#runTerminal`.\n */\n async #loadDataStream(\n defs: readonly RouteBranchDef<TMeta, TComponent>[],\n context: RouteContext<RouteParams, TRoutes>,\n signal: AbortSignal,\n isCurrent: () => boolean,\n location: RouteLocation,\n params: RouteParams,\n ): Promise<unknown[]> {\n const rawResults: Array<AsyncGenerator<unknown, unknown> | unknown> = defs.map((def) => {\n if (!def.dataFn) return undefined;\n\n const dataFn = def.dataFn as unknown as DataFn<RouteParams, TRoutes>;\n\n return dataFn({ ...context, signal } as DataContext<RouteParams, TRoutes>);\n });\n\n const streamingIndices: number[] = [];\n\n const settled: unknown[] = await Promise.all(\n rawResults.map(async (raw, i) => {\n if (isAsyncGenerator(raw)) {\n streamingIndices.push(i);\n\n return undefined;\n }\n\n if (raw === undefined) return undefined;\n\n try {\n return await (raw as Promise<unknown>);\n } catch (err) {\n const def = defs[i]!;\n\n if (def.onError) return def.onError(err, { ...context, signal } as unknown as DataContext);\n\n throw err;\n }\n }),\n );\n\n if (streamingIndices.length === 0) return settled;\n\n const streamingData: unknown[] = [...settled];\n\n const onPartial = (value: unknown, idx: number): void => {\n if (!isCurrent()) return;\n\n streamingData[idx] = value;\n\n const nodeStatuses: NavigationStatus[] = defs.map((_, i) =>\n streamingIndices.includes(i) ? 'streaming' : 'idle',\n );\n\n this.#currentState = createRouteState<TMeta, TComponent>({\n location,\n matches: buildMatchBranch(defs, params, location.pathname, streamingData, nodeStatuses),\n status: 'streaming',\n });\n this.#notifyListeners();\n };\n\n await Promise.all(\n streamingIndices.map(async (idx) => {\n const gen = rawResults[idx] as AsyncGenerator<unknown, unknown>;\n\n try {\n streamingData[idx] = await this.#runStreamingLoader(gen, idx, signal, isCurrent, onPartial);\n } catch (err) {\n const def = defs[idx]!;\n\n if (def.onError) {\n streamingData[idx] = await def.onError(err, { ...context, signal } as unknown as DataContext);\n } else {\n throw err;\n }\n }\n }),\n );\n\n return streamingData;\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<TMeta, TComponent>> {\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: preload ─────────────────────────────────────────────────────\n\n async #doPreload(startUrl: string, signal: AbortSignal, query?: QueryParams): Promise<void> {\n const prepared = await this.#resolveUrl(startUrl);\n\n if (prepared.type !== 'matched') return;\n\n const { location, params, record, resolvedQuery } = prepared;\n const defs = this.#hydration.effectiveDefs(record);\n const hasData = defs.some((d) => d.dataFn != null);\n\n if (!hasData) return;\n\n const branch = buildMatchBranch(\n defs,\n params,\n location.pathname,\n defs.map(() => undefined),\n );\n const context = createRouteContext<TRoutes>(location, resolvedQuery, params, branch, () => Promise.resolve());\n const results = await this.#loadDataDrain(defs, context, signal);\n\n this.#preload.set(buildPreloadKey(this.#base, record.path, params, query ?? location.query), results);\n }\n\n // ─── Private: route preparation ───────────────────────────────────────────\n\n async #prepareRoute(location: RouteLocation): Promise<NavigationDecision<TMeta, TComponent>> {\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 await this.#hydration.hydrate(record);\n\n const defs = this.#hydration.effectiveDefs(record);\n\n return {\n branch: buildMatchBranch(\n defs,\n params,\n location.pathname,\n defs.map(() => undefined),\n ),\n location,\n params,\n record,\n resolvedQuery,\n type: 'matched',\n };\n }\n\n // ─── Private: streaming data loader ──────────────────────────────────────\n\n /**\n * Drain an AsyncGenerator data loader, emitting `status: 'streaming'` for every yielded\n * partial value and returning the generator's return value as the final settled data.\n * Aborts cleanly when the signal fires or the navigation is superseded.\n */\n async #runStreamingLoader(\n generator: AsyncGenerator<unknown, unknown>,\n defIndex: number,\n signal: AbortSignal,\n isCurrent: () => boolean,\n onPartial: (data: unknown, defIndex: number) => void,\n ): Promise<unknown> {\n let lastValue: unknown;\n\n try {\n while (true) {\n if (signal.aborted || !isCurrent()) {\n await generator.return(undefined as unknown);\n\n return lastValue;\n }\n\n const { done, value } = await generator.next();\n\n if (done) return value ?? lastValue;\n\n lastValue = value;\n onPartial(value, defIndex);\n }\n } catch (err) {\n await generator.return(undefined as unknown).catch(() => undefined);\n throw err;\n }\n }\n\n // ─── Private: terminal (data only) ────────────────────────────────────────\n\n async #runTerminal(\n record: RouteRecord<TMeta, TComponent>,\n context: RouteContext<RouteParams, TRoutes>,\n location: RouteLocation,\n params: RouteParams,\n signal: AbortSignal,\n isCurrent: () => boolean,\n ): Promise<void> {\n if (!isCurrent()) return;\n\n const defs = this.#hydration.effectiveDefs(record);\n const hasData = defs.some((d) => d.dataFn != null);\n let dataResults: unknown[] = defs.map(() => undefined);\n\n if (hasData) {\n const preloadKey = buildPreloadKey(this.#base, record.path, params, location.query);\n const cached = this.#preload.consume(preloadKey);\n\n if (cached) {\n dataResults = cached;\n } else {\n // Emit per-node loading state while data is in-flight.\n const loadingStatuses: NavigationStatus[] = defs.map((d) => (d.dataFn ? 'loading' : 'idle'));\n\n this.#currentState = createRouteState<TMeta, TComponent>({\n location,\n matches: buildMatchBranch(defs, params, location.pathname, dataResults, loadingStatuses),\n status: 'loading',\n });\n this.#notifyListeners();\n\n try {\n dataResults = await this.#loadDataStream(defs, context, signal, isCurrent, location, params);\n } catch (error) {\n if (!isCurrent()) return;\n\n this.#currentState = createRouteState<TMeta, TComponent>({\n error,\n location,\n matches: buildMatchBranch(defs, params, location.pathname, dataResults),\n status: 'error',\n });\n // Attach enriched context so the eventual reporter uses it.\n attachErrorContext(error, { routeName: record.leaf.name, source: 'data-loader' });\n // Do not call #notifyListeners here — the finally block in #handleRoute does it once.\n throw error;\n }\n }\n }\n\n if (!isCurrent()) return;\n\n this.#currentState = createRouteState<TMeta, TComponent>({\n location,\n matches: buildMatchBranch(defs, params, location.pathname, dataResults),\n status: 'idle',\n });\n }\n\n // ─── Private: view transitions ────────────────────────────────────────────\n\n async #runWithTransition(run: () => Promise<void>, useTransition?: boolean): Promise<void> {\n const shouldUseTransition = useTransition ?? this.#useViewTransition;\n\n if (shouldUseTransition && typeof document !== 'undefined') {\n type ViewTransitionDocument = Document & {\n startViewTransition?: (callback: () => void | Promise<void>) => { finished: Promise<void> };\n };\n\n const doc = document as ViewTransitionDocument;\n\n if (doc.startViewTransition) {\n await doc.startViewTransition(run).finished;\n\n return;\n }\n }\n\n await run();\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 prevState = 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 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(this.#notFoundRecord!, context, currentLocation, {}, attempt.signal, isCurrent);\n },\n );\n };\n\n try {\n await this.#runWithTransition(run, useTransition);\n } finally {\n if (isCurrent() && committed) {\n this.#notifyListeners();\n this.#applyScroll(this.#currentState, prevState);\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<TMeta, TComponent>({\n location: prepared.location,\n matches: [] as RouteMatchBranch<TMeta, TComponent>,\n status: 'idle',\n });\n this.#notifyListeners();\n this.#applyScroll(this.#currentState, prevState);\n\n return true;\n }\n\n const { branch, location, params, record, resolvedQuery } = prepared;\n let committed = 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);\n },\n );\n };\n\n try {\n await this.#runWithTransition(run, useTransition);\n } finally {\n if (isCurrent() && committed) {\n this.#notifyListeners();\n this.#applyScroll(this.#currentState, prevState);\n }\n }\n\n return terminalRan && committed;\n }\n\n // ─── Private: scroll ─────────────────────────────────────────────────────\n\n #applyScroll(to: RouteState<TMeta, TComponent>, from: RouteState<TMeta, TComponent>): 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 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 }\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, TMeta = unknown, TComponent = unknown>(\n options: RouterOptions<TRoutes, TMeta, TComponent>,\n): Router<TRoutes, TMeta, TComponent> {\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/router.d.ts
CHANGED
|
@@ -33,14 +33,13 @@ declare class Router<TRoutes extends RouteTable, TMeta = unknown, TComponent = u
|
|
|
33
33
|
/** Returns true when the current location matches the named route by prefix (default) or exactly. */
|
|
34
34
|
isActive<Name extends RouteName<TRoutes>>(name: Name, options?: IsActiveOptions): boolean;
|
|
35
35
|
/** Match a pathname to a route branch without running middleware or data loaders. Returns null for redirects or no match. */
|
|
36
|
-
|
|
36
|
+
match(pathname: string): RouteMatchBranch<TMeta, TComponent> | null;
|
|
37
37
|
/**
|
|
38
38
|
* Load a URL into a route state including data loader results, without modifying
|
|
39
39
|
* router state or history. Follows declarative redirects and may resolve lazy modules.
|
|
40
|
-
*
|
|
41
|
-
* R5: Accepts an options object instead of a bare AbortSignal.
|
|
40
|
+
* Middleware is not executed — use `navigate()` when middleware side effects are needed.
|
|
42
41
|
*/
|
|
43
|
-
|
|
42
|
+
load(url: string, options?: {
|
|
44
43
|
signal?: AbortSignal;
|
|
45
44
|
}): Promise<RouteState<TMeta, TComponent> | null>;
|
|
46
45
|
/**
|
package/dist/router.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"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"}
|