@tanstack/router
Advanced tools
Comparing version 0.0.1-beta.61 to 0.0.1-beta.62
@@ -34,3 +34,5 @@ /** | ||
} | ||
init = () => { | ||
init = opts => { | ||
this.originalIndex = opts.originalIndex; | ||
this.router = opts.router; | ||
const allOptions = this.options; | ||
@@ -83,2 +85,5 @@ const isRoot = !allOptions?.path && !allOptions?.id; | ||
} | ||
static withRouterContext = () => { | ||
return options => new RootRoute(options); | ||
}; | ||
} | ||
@@ -85,0 +90,0 @@ |
@@ -35,4 +35,9 @@ /** | ||
status: 'idle' | ||
}, { | ||
onUpdate: next => { | ||
this.state = next; | ||
} | ||
}) | ||
}); | ||
this.state = this.store.state; | ||
if (!this.#hasLoaders()) { | ||
@@ -45,2 +50,56 @@ this.store.setState(s => ({ | ||
} | ||
#hasLoaders = () => { | ||
return !!(this.route.options.onLoad || componentTypes.some(d => this.route.options[d]?.preload)); | ||
}; | ||
__init = opts => { | ||
// Validate the search params and stabilize them | ||
this.parentMatch = opts.parentMatch; | ||
const parentSearch = this.parentMatch?.state.search ?? this.router.state.latestLocation.search; | ||
try { | ||
const validator = typeof this.route.options.validateSearch === 'object' ? this.route.options.validateSearch.parse : this.route.options.validateSearch; | ||
let nextSearch = validator?.(parentSearch) ?? {}; | ||
this.store.setState(s => ({ | ||
...s, | ||
routeSearch: nextSearch, | ||
search: { | ||
...parentSearch, | ||
...nextSearch | ||
} | ||
})); | ||
componentTypes.map(async type => { | ||
const component = this.route.options[type]; | ||
if (typeof this[type] !== 'function') { | ||
this[type] = component; | ||
} | ||
}); | ||
const parent = this.parentMatch; | ||
this.routeContext = this.route.options.getContext?.({ | ||
parentContext: parent?.routeContext, | ||
context: parent?.context, | ||
params: this.params, | ||
search: this.state.search | ||
}) || {}; | ||
this.context = parent ? { | ||
...parent.context, | ||
...this.routeContext | ||
} : { | ||
...this.router?.options.context, | ||
...this.routeContext | ||
}; | ||
} catch (err) { | ||
console.error(err); | ||
const error = new Error('Invalid search params found', { | ||
cause: err | ||
}); | ||
error.code = 'INVALID_SEARCH_PARAMS'; | ||
this.store.setState(s => ({ | ||
...s, | ||
status: 'error', | ||
error: error | ||
})); | ||
// Do not proceed with loading the route | ||
return; | ||
} | ||
}; | ||
cancel = () => { | ||
@@ -51,3 +110,3 @@ this.abortController?.abort(); | ||
// If the match is invalid, errored or idle, trigger it to load | ||
if (this.store.state.status !== 'pending') { | ||
if (this.state.status !== 'pending') { | ||
await this.fetch(opts); | ||
@@ -69,3 +128,3 @@ } | ||
// as loading or resolved | ||
if (this.store.state.status === 'idle') { | ||
if (this.state.status === 'idle') { | ||
this.store.setState(s => ({ | ||
@@ -92,5 +151,7 @@ ...s, | ||
params: this.params, | ||
search: this.store.state.search, | ||
search: this.state.search, | ||
signal: this.abortController.signal, | ||
preload: !!opts?.preload | ||
preload: !!opts?.preload, | ||
routeContext: this.routeContext, | ||
context: this.context | ||
}); | ||
@@ -123,46 +184,2 @@ } | ||
}; | ||
#hasLoaders = () => { | ||
return !!(this.route.options.onLoad || componentTypes.some(d => this.route.options[d]?.preload)); | ||
}; | ||
__setParentMatch = parentMatch => { | ||
if (!this.parentMatch && parentMatch) { | ||
this.parentMatch = parentMatch; | ||
} | ||
}; | ||
__validate = () => { | ||
// Validate the search params and stabilize them | ||
const parentSearch = this.parentMatch?.store.state.search ?? this.router.store.state.latestLocation.search; | ||
try { | ||
const validator = typeof this.route.options.validateSearch === 'object' ? this.route.options.validateSearch.parse : this.route.options.validateSearch; | ||
let nextSearch = validator?.(parentSearch) ?? {}; | ||
this.store.setState(s => ({ | ||
...s, | ||
routeSearch: nextSearch, | ||
search: { | ||
...parentSearch, | ||
...nextSearch | ||
} | ||
})); | ||
componentTypes.map(async type => { | ||
const component = this.route.options[type]; | ||
if (typeof this[type] !== 'function') { | ||
this[type] = component; | ||
} | ||
}); | ||
} catch (err) { | ||
console.error(err); | ||
const error = new Error('Invalid search params found', { | ||
cause: err | ||
}); | ||
error.code = 'INVALID_SEARCH_PARAMS'; | ||
this.store.setState(s => ({ | ||
...s, | ||
status: 'error', | ||
error: error | ||
})); | ||
// Do not proceed with loading the route | ||
return; | ||
} | ||
}; | ||
} | ||
@@ -169,0 +186,0 @@ |
@@ -62,3 +62,8 @@ /** | ||
}; | ||
this.store = new store.Store(getInitialRouterState()); | ||
this.store = new store.Store(getInitialRouterState(), { | ||
onUpdate: state => { | ||
this.state = state; | ||
} | ||
}); | ||
this.state = this.store.state; | ||
this.basepath = ''; | ||
@@ -77,3 +82,3 @@ this.update(options); | ||
// If the router matches are empty, load the matches | ||
if (!this.store.state.currentMatches.length) { | ||
if (!this.state.currentMatches.length) { | ||
this.load(); | ||
@@ -117,3 +122,5 @@ } | ||
this.#unsubHistory = this.history.listen(() => { | ||
this.load(this.#parseLocation(this.store.state.latestLocation)); | ||
this.load({ | ||
next: this.#parseLocation(this.state.latestLocation) | ||
}); | ||
}); | ||
@@ -144,7 +151,7 @@ } | ||
cancelMatches = () => { | ||
[...this.store.state.currentMatches, ...(this.store.state.pendingMatches || [])].forEach(match => { | ||
[...this.state.currentMatches, ...(this.state.pendingMatches || [])].forEach(match => { | ||
match.cancel(); | ||
}); | ||
}; | ||
load = async next => { | ||
load = async opts => { | ||
let now = Date.now(); | ||
@@ -158,7 +165,7 @@ const startedAt = now; | ||
this.store.batch(() => { | ||
if (next) { | ||
if (opts?.next) { | ||
// Ingest the new location | ||
this.store.setState(s => ({ | ||
...s, | ||
latestLocation: next | ||
latestLocation: opts.next | ||
})); | ||
@@ -168,3 +175,3 @@ } | ||
// Match the routes | ||
matches = this.matchRoutes(this.store.state.latestLocation.pathname, { | ||
matches = this.matchRoutes(this.state.latestLocation.pathname, { | ||
strictParseParams: true | ||
@@ -176,3 +183,3 @@ }); | ||
pendingMatches: matches, | ||
pendingLocation: this.store.state.latestLocation | ||
pendingLocation: this.state.latestLocation | ||
})); | ||
@@ -183,3 +190,5 @@ }); | ||
try { | ||
await this.loadMatches(matches); | ||
await this.loadMatches(matches | ||
// opts | ||
); | ||
} catch (err) { | ||
@@ -193,3 +202,3 @@ console.warn(err); | ||
} | ||
const previousMatches = this.store.state.currentMatches; | ||
const previousMatches = this.state.currentMatches; | ||
const exiting = [], | ||
@@ -211,7 +220,7 @@ staying = []; | ||
params: d.params, | ||
search: d.store.state.routeSearch | ||
search: d.state.routeSearch | ||
}); | ||
// Clear non-loading error states when match leaves | ||
if (d.store.state.status === 'error') { | ||
if (d.state.status === 'error') { | ||
this.store.setState(s => ({ | ||
@@ -227,3 +236,3 @@ ...s, | ||
params: d.params, | ||
search: d.store.state.routeSearch | ||
search: d.state.routeSearch | ||
}); | ||
@@ -234,11 +243,9 @@ }); | ||
params: d.params, | ||
search: d.store.state.search | ||
search: d.state.search | ||
}); | ||
// delete this.store.state.matchCache[d.id] // TODO: | ||
}); | ||
this.store.setState(s => ({ | ||
...s, | ||
status: 'idle', | ||
currentLocation: this.store.state.latestLocation, | ||
currentLocation: this.state.latestLocation, | ||
currentMatches: matches, | ||
@@ -256,3 +263,3 @@ pendingLocation: undefined, | ||
}; | ||
loadRoute = async (navigateOpts = this.store.state.latestLocation) => { | ||
loadRoute = async (navigateOpts = this.state.latestLocation) => { | ||
const next = this.buildNext(navigateOpts); | ||
@@ -265,3 +272,3 @@ const matches = this.matchRoutes(next.pathname, { | ||
}; | ||
preloadRoute = async (navigateOpts = this.store.state.latestLocation) => { | ||
preloadRoute = async (navigateOpts = this.state.latestLocation) => { | ||
const next = this.buildNext(navigateOpts); | ||
@@ -281,3 +288,3 @@ const matches = this.matchRoutes(next.pathname, { | ||
} | ||
const existingMatches = [...this.store.state.currentMatches, ...(this.store.state.pendingMatches ?? [])]; | ||
const existingMatches = [...this.state.currentMatches, ...(this.state.pendingMatches ?? [])]; | ||
const findInRouteTree = async routes => { | ||
@@ -328,5 +335,3 @@ const parentMatch = utils.last(matches); | ||
const matchId = path.interpolatePath(foundRoute.id, params, true); | ||
const match = existingMatches.find(d => d.id === matchId) || | ||
// this.store.state.matchCache[matchId]?.match || // TODO: | ||
new routeMatch.RouteMatch(this, foundRoute, { | ||
const match = existingMatches.find(d => d.id === matchId) || new routeMatch.RouteMatch(this, foundRoute, { | ||
id: matchId, | ||
@@ -347,11 +352,5 @@ params, | ||
}; | ||
loadMatches = async (resolvedMatches, loaderOpts) => { | ||
linkMatches(resolvedMatches); | ||
loadMatches = async (resolvedMatches, opts) => { | ||
initMatches(resolvedMatches); | ||
// this.cleanMatchCache() | ||
resolvedMatches.forEach(async match => { | ||
// Validate the match (loads search params etc) | ||
match.__validate(); | ||
}); | ||
// Check each match middleware to see if the route can be accessed | ||
@@ -365,3 +364,3 @@ await Promise.all(resolvedMatches.map(async match => { | ||
} catch (err) { | ||
if (!loaderOpts?.preload) { | ||
if (!opts?.preload) { | ||
match.route.options.onLoadError?.(err); | ||
@@ -374,10 +373,12 @@ } | ||
const prevMatch = resolvedMatches[1]; | ||
const search = match.store.state.search; | ||
if (search.__data?.matchId && search.__data.matchId !== match.id) { | ||
return; | ||
} | ||
match.state.search; | ||
// if (opts?.filter && !opts.filter(match)) { | ||
// return | ||
// } | ||
match.load({ | ||
preload: loaderOpts?.preload | ||
preload: opts?.preload | ||
}); | ||
if (match.store.state.status !== 'success' && match.__loadPromise) { | ||
if (match.state.status !== 'success' && match.__loadPromise) { | ||
// Wait for the first sign of activity from the match | ||
@@ -439,6 +440,6 @@ await match.__loadPromise; | ||
if (opts?.pending) { | ||
if (!this.store.state.pendingLocation) { | ||
if (!this.state.pendingLocation) { | ||
return false; | ||
} | ||
return path.matchPathname(this.basepath, this.store.state.pendingLocation.pathname, { | ||
return path.matchPathname(this.basepath, this.state.pendingLocation.pathname, { | ||
...opts, | ||
@@ -448,3 +449,3 @@ to: next.pathname | ||
} | ||
return path.matchPathname(this.basepath, this.store.state.currentLocation.pathname, { | ||
return path.matchPathname(this.basepath, this.state.currentLocation.pathname, { | ||
...opts, | ||
@@ -495,7 +496,7 @@ to: next.pathname | ||
// Compare path/hash for matches | ||
const pathIsEqual = this.store.state.currentLocation.pathname === next.pathname; | ||
const currentPathSplit = this.store.state.currentLocation.pathname.split('/'); | ||
const pathIsEqual = this.state.currentLocation.pathname === next.pathname; | ||
const currentPathSplit = this.state.currentLocation.pathname.split('/'); | ||
const nextPathSplit = next.pathname.split('/'); | ||
const pathIsFuzzyEqual = nextPathSplit.every((d, i) => d === currentPathSplit[i]); | ||
const hashIsEqual = this.store.state.currentLocation.hash === next.hash; | ||
const hashIsEqual = this.state.currentLocation.hash === next.hash; | ||
// Combine the matches based on user options | ||
@@ -563,11 +564,10 @@ const pathTest = activeOptions?.exact ? pathIsEqual : pathIsFuzzyEqual; | ||
state: { | ||
...utils.pick(this.store.state, ['latestLocation', 'currentLocation', 'status', 'lastUpdated']), | ||
currentMatches: this.store.state.currentMatches.map(match => ({ | ||
...utils.pick(this.state, ['latestLocation', 'currentLocation', 'status', 'lastUpdated']), | ||
currentMatches: this.state.currentMatches.map(match => ({ | ||
id: match.id, | ||
state: { | ||
...utils.pick(match.store.state, ['status']) | ||
status: match.state.status | ||
} | ||
})) | ||
}, | ||
context: this.options.context | ||
} | ||
}; | ||
@@ -577,4 +577,2 @@ }; | ||
this.store.setState(s => { | ||
this.options.context = dehydratedRouter.context; | ||
// Match the routes | ||
@@ -592,3 +590,3 @@ const currentMatches = this.matchRoutes(dehydratedRouter.state.latestLocation.pathname, { | ||
}); | ||
currentMatches.forEach(match => match.__validate()); | ||
initMatches(currentMatches); | ||
return { | ||
@@ -604,5 +602,6 @@ ...s, | ||
routes.forEach((route, i) => { | ||
route.init(); | ||
route.originalIndex = i; | ||
route.router = this; | ||
route.init({ | ||
originalIndex: i, | ||
router: this | ||
}); | ||
const existingRoute = this.routesById[route.id]; | ||
@@ -645,5 +644,5 @@ if (existingRoute) { | ||
#buildLocation = (dest = {}) => { | ||
const fromPathname = dest.fromCurrent ? this.store.state.latestLocation.pathname : dest.from ?? this.store.state.latestLocation.pathname; | ||
const fromPathname = dest.fromCurrent ? this.state.latestLocation.pathname : dest.from ?? this.state.latestLocation.pathname; | ||
let pathname = path.resolvePath(this.basepath ?? '/', fromPathname, `${dest.to ?? '.'}`); | ||
const fromMatches = this.matchRoutes(this.store.state.latestLocation.pathname, { | ||
const fromMatches = this.matchRoutes(this.state.latestLocation.pathname, { | ||
strictParseParams: true | ||
@@ -664,3 +663,3 @@ }); | ||
// Pre filters first | ||
const preFilteredSearch = dest.__preSearchFilters?.length ? dest.__preSearchFilters?.reduce((prev, next) => next(prev), this.store.state.latestLocation.search) : this.store.state.latestLocation.search; | ||
const preFilteredSearch = dest.__preSearchFilters?.length ? dest.__preSearchFilters?.reduce((prev, next) => next(prev), this.state.latestLocation.search) : this.state.latestLocation.search; | ||
@@ -675,5 +674,5 @@ // Then the link/navigate function | ||
const postFilteredSearch = dest.__postSearchFilters?.length ? dest.__postSearchFilters.reduce((prev, next) => next(prev), destSearch) : destSearch; | ||
const search = utils.replaceEqualDeep(this.store.state.latestLocation.search, postFilteredSearch); | ||
const search = utils.replaceEqualDeep(this.state.latestLocation.search, postFilteredSearch); | ||
const searchStr = this.options.stringifySearch(search); | ||
let hash = dest.hash === true ? this.store.state.latestLocation.hash : utils.functionalUpdate(dest.hash, this.store.state.latestLocation.hash); | ||
let hash = dest.hash === true ? this.state.latestLocation.hash : utils.functionalUpdate(dest.hash, this.state.latestLocation.hash); | ||
hash = hash ? `#${hash}` : ''; | ||
@@ -684,3 +683,3 @@ return { | ||
searchStr, | ||
state: this.store.state.latestLocation.state, | ||
state: this.state.latestLocation.state, | ||
hash, | ||
@@ -699,3 +698,3 @@ href: `${pathname}${searchStr}${hash}`, | ||
} | ||
const isSameUrl = this.store.state.latestLocation.href === next.href; | ||
const isSameUrl = this.state.latestLocation.href === next.href; | ||
if (isSameUrl && !next.key) { | ||
@@ -710,3 +709,3 @@ nextAction = 'replace'; | ||
// this.load(this.#parseLocation(this.store.state.latestLocation)) | ||
// this.load(this.#parseLocation(this.state.latestLocation)) | ||
@@ -737,8 +736,8 @@ return this.navigationPromise = new Promise(resolve => { | ||
} | ||
function linkMatches(matches) { | ||
function initMatches(matches) { | ||
matches.forEach((match, index) => { | ||
const parent = matches[index - 1]; | ||
if (parent) { | ||
match.__setParentMatch(parent); | ||
} | ||
const parentMatch = matches[index - 1]; | ||
match.__init({ | ||
parentMatch | ||
}); | ||
}); | ||
@@ -745,0 +744,0 @@ } |
@@ -473,3 +473,5 @@ /** | ||
} | ||
init = () => { | ||
init = opts => { | ||
this.originalIndex = opts.originalIndex; | ||
this.router = opts.router; | ||
const allOptions = this.options; | ||
@@ -522,2 +524,5 @@ const isRoot = !allOptions?.path && !allOptions?.id; | ||
} | ||
static withRouterContext = () => { | ||
return options => new RootRoute(options); | ||
}; | ||
} | ||
@@ -587,4 +592,9 @@ | ||
status: 'idle' | ||
}, { | ||
onUpdate: next => { | ||
this.state = next; | ||
} | ||
}) | ||
}); | ||
this.state = this.store.state; | ||
if (!this.#hasLoaders()) { | ||
@@ -597,2 +607,56 @@ this.store.setState(s => ({ | ||
} | ||
#hasLoaders = () => { | ||
return !!(this.route.options.onLoad || componentTypes.some(d => this.route.options[d]?.preload)); | ||
}; | ||
__init = opts => { | ||
// Validate the search params and stabilize them | ||
this.parentMatch = opts.parentMatch; | ||
const parentSearch = this.parentMatch?.state.search ?? this.router.state.latestLocation.search; | ||
try { | ||
const validator = typeof this.route.options.validateSearch === 'object' ? this.route.options.validateSearch.parse : this.route.options.validateSearch; | ||
let nextSearch = validator?.(parentSearch) ?? {}; | ||
this.store.setState(s => ({ | ||
...s, | ||
routeSearch: nextSearch, | ||
search: { | ||
...parentSearch, | ||
...nextSearch | ||
} | ||
})); | ||
componentTypes.map(async type => { | ||
const component = this.route.options[type]; | ||
if (typeof this[type] !== 'function') { | ||
this[type] = component; | ||
} | ||
}); | ||
const parent = this.parentMatch; | ||
this.routeContext = this.route.options.getContext?.({ | ||
parentContext: parent?.routeContext, | ||
context: parent?.context, | ||
params: this.params, | ||
search: this.state.search | ||
}) || {}; | ||
this.context = parent ? { | ||
...parent.context, | ||
...this.routeContext | ||
} : { | ||
...this.router?.options.context, | ||
...this.routeContext | ||
}; | ||
} catch (err) { | ||
console.error(err); | ||
const error = new Error('Invalid search params found', { | ||
cause: err | ||
}); | ||
error.code = 'INVALID_SEARCH_PARAMS'; | ||
this.store.setState(s => ({ | ||
...s, | ||
status: 'error', | ||
error: error | ||
})); | ||
// Do not proceed with loading the route | ||
return; | ||
} | ||
}; | ||
cancel = () => { | ||
@@ -603,3 +667,3 @@ this.abortController?.abort(); | ||
// If the match is invalid, errored or idle, trigger it to load | ||
if (this.store.state.status !== 'pending') { | ||
if (this.state.status !== 'pending') { | ||
await this.fetch(opts); | ||
@@ -621,3 +685,3 @@ } | ||
// as loading or resolved | ||
if (this.store.state.status === 'idle') { | ||
if (this.state.status === 'idle') { | ||
this.store.setState(s => ({ | ||
@@ -644,5 +708,7 @@ ...s, | ||
params: this.params, | ||
search: this.store.state.search, | ||
search: this.state.search, | ||
signal: this.abortController.signal, | ||
preload: !!opts?.preload | ||
preload: !!opts?.preload, | ||
routeContext: this.routeContext, | ||
context: this.context | ||
}); | ||
@@ -675,46 +741,2 @@ } | ||
}; | ||
#hasLoaders = () => { | ||
return !!(this.route.options.onLoad || componentTypes.some(d => this.route.options[d]?.preload)); | ||
}; | ||
__setParentMatch = parentMatch => { | ||
if (!this.parentMatch && parentMatch) { | ||
this.parentMatch = parentMatch; | ||
} | ||
}; | ||
__validate = () => { | ||
// Validate the search params and stabilize them | ||
const parentSearch = this.parentMatch?.store.state.search ?? this.router.store.state.latestLocation.search; | ||
try { | ||
const validator = typeof this.route.options.validateSearch === 'object' ? this.route.options.validateSearch.parse : this.route.options.validateSearch; | ||
let nextSearch = validator?.(parentSearch) ?? {}; | ||
this.store.setState(s => ({ | ||
...s, | ||
routeSearch: nextSearch, | ||
search: { | ||
...parentSearch, | ||
...nextSearch | ||
} | ||
})); | ||
componentTypes.map(async type => { | ||
const component = this.route.options[type]; | ||
if (typeof this[type] !== 'function') { | ||
this[type] = component; | ||
} | ||
}); | ||
} catch (err) { | ||
console.error(err); | ||
const error = new Error('Invalid search params found', { | ||
cause: err | ||
}); | ||
error.code = 'INVALID_SEARCH_PARAMS'; | ||
this.store.setState(s => ({ | ||
...s, | ||
status: 'error', | ||
error: error | ||
})); | ||
// Do not proceed with loading the route | ||
return; | ||
} | ||
}; | ||
} | ||
@@ -804,3 +826,8 @@ | ||
}; | ||
this.store = new Store(getInitialRouterState()); | ||
this.store = new Store(getInitialRouterState(), { | ||
onUpdate: state => { | ||
this.state = state; | ||
} | ||
}); | ||
this.state = this.store.state; | ||
this.basepath = ''; | ||
@@ -819,3 +846,3 @@ this.update(options); | ||
// If the router matches are empty, load the matches | ||
if (!this.store.state.currentMatches.length) { | ||
if (!this.state.currentMatches.length) { | ||
this.load(); | ||
@@ -859,3 +886,5 @@ } | ||
this.#unsubHistory = this.history.listen(() => { | ||
this.load(this.#parseLocation(this.store.state.latestLocation)); | ||
this.load({ | ||
next: this.#parseLocation(this.state.latestLocation) | ||
}); | ||
}); | ||
@@ -886,7 +915,7 @@ } | ||
cancelMatches = () => { | ||
[...this.store.state.currentMatches, ...(this.store.state.pendingMatches || [])].forEach(match => { | ||
[...this.state.currentMatches, ...(this.state.pendingMatches || [])].forEach(match => { | ||
match.cancel(); | ||
}); | ||
}; | ||
load = async next => { | ||
load = async opts => { | ||
let now = Date.now(); | ||
@@ -900,7 +929,7 @@ const startedAt = now; | ||
this.store.batch(() => { | ||
if (next) { | ||
if (opts?.next) { | ||
// Ingest the new location | ||
this.store.setState(s => ({ | ||
...s, | ||
latestLocation: next | ||
latestLocation: opts.next | ||
})); | ||
@@ -910,3 +939,3 @@ } | ||
// Match the routes | ||
matches = this.matchRoutes(this.store.state.latestLocation.pathname, { | ||
matches = this.matchRoutes(this.state.latestLocation.pathname, { | ||
strictParseParams: true | ||
@@ -918,3 +947,3 @@ }); | ||
pendingMatches: matches, | ||
pendingLocation: this.store.state.latestLocation | ||
pendingLocation: this.state.latestLocation | ||
})); | ||
@@ -925,3 +954,5 @@ }); | ||
try { | ||
await this.loadMatches(matches); | ||
await this.loadMatches(matches | ||
// opts | ||
); | ||
} catch (err) { | ||
@@ -935,3 +966,3 @@ console.warn(err); | ||
} | ||
const previousMatches = this.store.state.currentMatches; | ||
const previousMatches = this.state.currentMatches; | ||
const exiting = [], | ||
@@ -953,7 +984,7 @@ staying = []; | ||
params: d.params, | ||
search: d.store.state.routeSearch | ||
search: d.state.routeSearch | ||
}); | ||
// Clear non-loading error states when match leaves | ||
if (d.store.state.status === 'error') { | ||
if (d.state.status === 'error') { | ||
this.store.setState(s => ({ | ||
@@ -969,3 +1000,3 @@ ...s, | ||
params: d.params, | ||
search: d.store.state.routeSearch | ||
search: d.state.routeSearch | ||
}); | ||
@@ -976,11 +1007,9 @@ }); | ||
params: d.params, | ||
search: d.store.state.search | ||
search: d.state.search | ||
}); | ||
// delete this.store.state.matchCache[d.id] // TODO: | ||
}); | ||
this.store.setState(s => ({ | ||
...s, | ||
status: 'idle', | ||
currentLocation: this.store.state.latestLocation, | ||
currentLocation: this.state.latestLocation, | ||
currentMatches: matches, | ||
@@ -998,3 +1027,3 @@ pendingLocation: undefined, | ||
}; | ||
loadRoute = async (navigateOpts = this.store.state.latestLocation) => { | ||
loadRoute = async (navigateOpts = this.state.latestLocation) => { | ||
const next = this.buildNext(navigateOpts); | ||
@@ -1007,3 +1036,3 @@ const matches = this.matchRoutes(next.pathname, { | ||
}; | ||
preloadRoute = async (navigateOpts = this.store.state.latestLocation) => { | ||
preloadRoute = async (navigateOpts = this.state.latestLocation) => { | ||
const next = this.buildNext(navigateOpts); | ||
@@ -1023,3 +1052,3 @@ const matches = this.matchRoutes(next.pathname, { | ||
} | ||
const existingMatches = [...this.store.state.currentMatches, ...(this.store.state.pendingMatches ?? [])]; | ||
const existingMatches = [...this.state.currentMatches, ...(this.state.pendingMatches ?? [])]; | ||
const findInRouteTree = async routes => { | ||
@@ -1070,5 +1099,3 @@ const parentMatch = last(matches); | ||
const matchId = interpolatePath(foundRoute.id, params, true); | ||
const match = existingMatches.find(d => d.id === matchId) || | ||
// this.store.state.matchCache[matchId]?.match || // TODO: | ||
new RouteMatch(this, foundRoute, { | ||
const match = existingMatches.find(d => d.id === matchId) || new RouteMatch(this, foundRoute, { | ||
id: matchId, | ||
@@ -1089,11 +1116,5 @@ params, | ||
}; | ||
loadMatches = async (resolvedMatches, loaderOpts) => { | ||
linkMatches(resolvedMatches); | ||
loadMatches = async (resolvedMatches, opts) => { | ||
initMatches(resolvedMatches); | ||
// this.cleanMatchCache() | ||
resolvedMatches.forEach(async match => { | ||
// Validate the match (loads search params etc) | ||
match.__validate(); | ||
}); | ||
// Check each match middleware to see if the route can be accessed | ||
@@ -1107,3 +1128,3 @@ await Promise.all(resolvedMatches.map(async match => { | ||
} catch (err) { | ||
if (!loaderOpts?.preload) { | ||
if (!opts?.preload) { | ||
match.route.options.onLoadError?.(err); | ||
@@ -1116,10 +1137,12 @@ } | ||
const prevMatch = resolvedMatches[1]; | ||
const search = match.store.state.search; | ||
if (search.__data?.matchId && search.__data.matchId !== match.id) { | ||
return; | ||
} | ||
match.state.search; | ||
// if (opts?.filter && !opts.filter(match)) { | ||
// return | ||
// } | ||
match.load({ | ||
preload: loaderOpts?.preload | ||
preload: opts?.preload | ||
}); | ||
if (match.store.state.status !== 'success' && match.__loadPromise) { | ||
if (match.state.status !== 'success' && match.__loadPromise) { | ||
// Wait for the first sign of activity from the match | ||
@@ -1181,6 +1204,6 @@ await match.__loadPromise; | ||
if (opts?.pending) { | ||
if (!this.store.state.pendingLocation) { | ||
if (!this.state.pendingLocation) { | ||
return false; | ||
} | ||
return matchPathname(this.basepath, this.store.state.pendingLocation.pathname, { | ||
return matchPathname(this.basepath, this.state.pendingLocation.pathname, { | ||
...opts, | ||
@@ -1190,3 +1213,3 @@ to: next.pathname | ||
} | ||
return matchPathname(this.basepath, this.store.state.currentLocation.pathname, { | ||
return matchPathname(this.basepath, this.state.currentLocation.pathname, { | ||
...opts, | ||
@@ -1237,7 +1260,7 @@ to: next.pathname | ||
// Compare path/hash for matches | ||
const pathIsEqual = this.store.state.currentLocation.pathname === next.pathname; | ||
const currentPathSplit = this.store.state.currentLocation.pathname.split('/'); | ||
const pathIsEqual = this.state.currentLocation.pathname === next.pathname; | ||
const currentPathSplit = this.state.currentLocation.pathname.split('/'); | ||
const nextPathSplit = next.pathname.split('/'); | ||
const pathIsFuzzyEqual = nextPathSplit.every((d, i) => d === currentPathSplit[i]); | ||
const hashIsEqual = this.store.state.currentLocation.hash === next.hash; | ||
const hashIsEqual = this.state.currentLocation.hash === next.hash; | ||
// Combine the matches based on user options | ||
@@ -1305,11 +1328,10 @@ const pathTest = activeOptions?.exact ? pathIsEqual : pathIsFuzzyEqual; | ||
state: { | ||
...pick(this.store.state, ['latestLocation', 'currentLocation', 'status', 'lastUpdated']), | ||
currentMatches: this.store.state.currentMatches.map(match => ({ | ||
...pick(this.state, ['latestLocation', 'currentLocation', 'status', 'lastUpdated']), | ||
currentMatches: this.state.currentMatches.map(match => ({ | ||
id: match.id, | ||
state: { | ||
...pick(match.store.state, ['status']) | ||
status: match.state.status | ||
} | ||
})) | ||
}, | ||
context: this.options.context | ||
} | ||
}; | ||
@@ -1319,4 +1341,2 @@ }; | ||
this.store.setState(s => { | ||
this.options.context = dehydratedRouter.context; | ||
// Match the routes | ||
@@ -1334,3 +1354,3 @@ const currentMatches = this.matchRoutes(dehydratedRouter.state.latestLocation.pathname, { | ||
}); | ||
currentMatches.forEach(match => match.__validate()); | ||
initMatches(currentMatches); | ||
return { | ||
@@ -1346,5 +1366,6 @@ ...s, | ||
routes.forEach((route, i) => { | ||
route.init(); | ||
route.originalIndex = i; | ||
route.router = this; | ||
route.init({ | ||
originalIndex: i, | ||
router: this | ||
}); | ||
const existingRoute = this.routesById[route.id]; | ||
@@ -1387,5 +1408,5 @@ if (existingRoute) { | ||
#buildLocation = (dest = {}) => { | ||
const fromPathname = dest.fromCurrent ? this.store.state.latestLocation.pathname : dest.from ?? this.store.state.latestLocation.pathname; | ||
const fromPathname = dest.fromCurrent ? this.state.latestLocation.pathname : dest.from ?? this.state.latestLocation.pathname; | ||
let pathname = resolvePath(this.basepath ?? '/', fromPathname, `${dest.to ?? '.'}`); | ||
const fromMatches = this.matchRoutes(this.store.state.latestLocation.pathname, { | ||
const fromMatches = this.matchRoutes(this.state.latestLocation.pathname, { | ||
strictParseParams: true | ||
@@ -1406,3 +1427,3 @@ }); | ||
// Pre filters first | ||
const preFilteredSearch = dest.__preSearchFilters?.length ? dest.__preSearchFilters?.reduce((prev, next) => next(prev), this.store.state.latestLocation.search) : this.store.state.latestLocation.search; | ||
const preFilteredSearch = dest.__preSearchFilters?.length ? dest.__preSearchFilters?.reduce((prev, next) => next(prev), this.state.latestLocation.search) : this.state.latestLocation.search; | ||
@@ -1417,5 +1438,5 @@ // Then the link/navigate function | ||
const postFilteredSearch = dest.__postSearchFilters?.length ? dest.__postSearchFilters.reduce((prev, next) => next(prev), destSearch) : destSearch; | ||
const search = replaceEqualDeep(this.store.state.latestLocation.search, postFilteredSearch); | ||
const search = replaceEqualDeep(this.state.latestLocation.search, postFilteredSearch); | ||
const searchStr = this.options.stringifySearch(search); | ||
let hash = dest.hash === true ? this.store.state.latestLocation.hash : functionalUpdate(dest.hash, this.store.state.latestLocation.hash); | ||
let hash = dest.hash === true ? this.state.latestLocation.hash : functionalUpdate(dest.hash, this.state.latestLocation.hash); | ||
hash = hash ? `#${hash}` : ''; | ||
@@ -1426,3 +1447,3 @@ return { | ||
searchStr, | ||
state: this.store.state.latestLocation.state, | ||
state: this.state.latestLocation.state, | ||
hash, | ||
@@ -1441,3 +1462,3 @@ href: `${pathname}${searchStr}${hash}`, | ||
} | ||
const isSameUrl = this.store.state.latestLocation.href === next.href; | ||
const isSameUrl = this.state.latestLocation.href === next.href; | ||
if (isSameUrl && !next.key) { | ||
@@ -1452,3 +1473,3 @@ nextAction = 'replace'; | ||
// this.load(this.#parseLocation(this.store.state.latestLocation)) | ||
// this.load(this.#parseLocation(this.state.latestLocation)) | ||
@@ -1479,8 +1500,8 @@ return this.navigationPromise = new Promise(resolve => { | ||
} | ||
function linkMatches(matches) { | ||
function initMatches(matches) { | ||
matches.forEach((match, index) => { | ||
const parent = matches[index - 1]; | ||
if (parent) { | ||
match.__setParentMatch(parent); | ||
} | ||
const parentMatch = matches[index - 1]; | ||
match.__init({ | ||
parentMatch | ||
}); | ||
}); | ||
@@ -1487,0 +1508,0 @@ } |
@@ -11,3 +11,3 @@ { | ||
"name": "node_modules/.pnpm/tiny-invariant@1.3.1/node_modules/tiny-invariant/dist/esm/tiny-invariant.js", | ||
"uid": "eda2-30" | ||
"uid": "dcf0-30" | ||
}, | ||
@@ -21,35 +21,35 @@ { | ||
{ | ||
"uid": "eda2-32", | ||
"uid": "dcf0-32", | ||
"name": "history.ts" | ||
}, | ||
{ | ||
"uid": "eda2-34", | ||
"uid": "dcf0-34", | ||
"name": "utils.ts" | ||
}, | ||
{ | ||
"uid": "eda2-36", | ||
"uid": "dcf0-36", | ||
"name": "path.ts" | ||
}, | ||
{ | ||
"uid": "eda2-38", | ||
"uid": "dcf0-38", | ||
"name": "qss.ts" | ||
}, | ||
{ | ||
"uid": "eda2-40", | ||
"uid": "dcf0-40", | ||
"name": "route.ts" | ||
}, | ||
{ | ||
"uid": "eda2-44", | ||
"uid": "dcf0-44", | ||
"name": "routeMatch.ts" | ||
}, | ||
{ | ||
"uid": "eda2-46", | ||
"uid": "dcf0-46", | ||
"name": "searchParams.ts" | ||
}, | ||
{ | ||
"uid": "eda2-48", | ||
"uid": "dcf0-48", | ||
"name": "router.ts" | ||
}, | ||
{ | ||
"uid": "eda2-50", | ||
"uid": "dcf0-50", | ||
"name": "index.ts" | ||
@@ -61,3 +61,3 @@ } | ||
"name": "store/build/esm/index.js", | ||
"uid": "eda2-42" | ||
"uid": "dcf0-42" | ||
} | ||
@@ -72,74 +72,74 @@ ] | ||
"nodeParts": { | ||
"eda2-30": { | ||
"dcf0-30": { | ||
"renderedLength": 199, | ||
"gzipLength": 134, | ||
"brotliLength": 0, | ||
"mainUid": "eda2-29" | ||
"mainUid": "dcf0-29" | ||
}, | ||
"eda2-32": { | ||
"dcf0-32": { | ||
"renderedLength": 4236, | ||
"gzipLength": 1085, | ||
"brotliLength": 0, | ||
"mainUid": "eda2-31" | ||
"mainUid": "dcf0-31" | ||
}, | ||
"eda2-34": { | ||
"dcf0-34": { | ||
"renderedLength": 2572, | ||
"gzipLength": 950, | ||
"brotliLength": 0, | ||
"mainUid": "eda2-33" | ||
"mainUid": "dcf0-33" | ||
}, | ||
"eda2-36": { | ||
"dcf0-36": { | ||
"renderedLength": 5601, | ||
"gzipLength": 1328, | ||
"brotliLength": 0, | ||
"mainUid": "eda2-35" | ||
"mainUid": "dcf0-35" | ||
}, | ||
"eda2-38": { | ||
"dcf0-38": { | ||
"renderedLength": 1395, | ||
"gzipLength": 558, | ||
"brotliLength": 0, | ||
"mainUid": "eda2-37" | ||
"mainUid": "dcf0-37" | ||
}, | ||
"eda2-40": { | ||
"renderedLength": 2962, | ||
"gzipLength": 894, | ||
"dcf0-40": { | ||
"renderedLength": 3148, | ||
"gzipLength": 944, | ||
"brotliLength": 0, | ||
"mainUid": "eda2-39" | ||
"mainUid": "dcf0-39" | ||
}, | ||
"eda2-42": { | ||
"renderedLength": 1386, | ||
"gzipLength": 501, | ||
"dcf0-42": { | ||
"renderedLength": 1384, | ||
"gzipLength": 502, | ||
"brotliLength": 0, | ||
"mainUid": "eda2-41" | ||
"mainUid": "dcf0-41" | ||
}, | ||
"eda2-44": { | ||
"renderedLength": 4873, | ||
"gzipLength": 1357, | ||
"dcf0-44": { | ||
"renderedLength": 5448, | ||
"gzipLength": 1470, | ||
"brotliLength": 0, | ||
"mainUid": "eda2-43" | ||
"mainUid": "dcf0-43" | ||
}, | ||
"eda2-46": { | ||
"dcf0-46": { | ||
"renderedLength": 1387, | ||
"gzipLength": 483, | ||
"brotliLength": 0, | ||
"mainUid": "eda2-45" | ||
"mainUid": "dcf0-45" | ||
}, | ||
"eda2-48": { | ||
"renderedLength": 23650, | ||
"gzipLength": 5501, | ||
"dcf0-48": { | ||
"renderedLength": 23107, | ||
"gzipLength": 5390, | ||
"brotliLength": 0, | ||
"mainUid": "eda2-47" | ||
"mainUid": "dcf0-47" | ||
}, | ||
"eda2-50": { | ||
"dcf0-50": { | ||
"renderedLength": 0, | ||
"gzipLength": 0, | ||
"brotliLength": 0, | ||
"mainUid": "eda2-49" | ||
"mainUid": "dcf0-49" | ||
} | ||
}, | ||
"nodeMetas": { | ||
"eda2-29": { | ||
"dcf0-29": { | ||
"id": "/node_modules/.pnpm/tiny-invariant@1.3.1/node_modules/tiny-invariant/dist/esm/tiny-invariant.js", | ||
"moduleParts": { | ||
"index.production.js": "eda2-30" | ||
"index.production.js": "dcf0-30" | ||
}, | ||
@@ -149,16 +149,16 @@ "imported": [], | ||
{ | ||
"uid": "eda2-49" | ||
"uid": "dcf0-49" | ||
}, | ||
{ | ||
"uid": "eda2-39" | ||
"uid": "dcf0-39" | ||
}, | ||
{ | ||
"uid": "eda2-47" | ||
"uid": "dcf0-47" | ||
} | ||
] | ||
}, | ||
"eda2-31": { | ||
"dcf0-31": { | ||
"id": "/packages/router/src/history.ts", | ||
"moduleParts": { | ||
"index.production.js": "eda2-32" | ||
"index.production.js": "dcf0-32" | ||
}, | ||
@@ -168,13 +168,13 @@ "imported": [], | ||
{ | ||
"uid": "eda2-49" | ||
"uid": "dcf0-49" | ||
}, | ||
{ | ||
"uid": "eda2-47" | ||
"uid": "dcf0-47" | ||
} | ||
] | ||
}, | ||
"eda2-33": { | ||
"dcf0-33": { | ||
"id": "/packages/router/src/utils.ts", | ||
"moduleParts": { | ||
"index.production.js": "eda2-34" | ||
"index.production.js": "dcf0-34" | ||
}, | ||
@@ -184,20 +184,20 @@ "imported": [], | ||
{ | ||
"uid": "eda2-49" | ||
"uid": "dcf0-49" | ||
}, | ||
{ | ||
"uid": "eda2-35" | ||
"uid": "dcf0-35" | ||
}, | ||
{ | ||
"uid": "eda2-47" | ||
"uid": "dcf0-47" | ||
} | ||
] | ||
}, | ||
"eda2-35": { | ||
"dcf0-35": { | ||
"id": "/packages/router/src/path.ts", | ||
"moduleParts": { | ||
"index.production.js": "eda2-36" | ||
"index.production.js": "dcf0-36" | ||
}, | ||
"imported": [ | ||
{ | ||
"uid": "eda2-33" | ||
"uid": "dcf0-33" | ||
} | ||
@@ -207,16 +207,16 @@ ], | ||
{ | ||
"uid": "eda2-49" | ||
"uid": "dcf0-49" | ||
}, | ||
{ | ||
"uid": "eda2-39" | ||
"uid": "dcf0-39" | ||
}, | ||
{ | ||
"uid": "eda2-47" | ||
"uid": "dcf0-47" | ||
} | ||
] | ||
}, | ||
"eda2-37": { | ||
"dcf0-37": { | ||
"id": "/packages/router/src/qss.ts", | ||
"moduleParts": { | ||
"index.production.js": "eda2-38" | ||
"index.production.js": "dcf0-38" | ||
}, | ||
@@ -226,20 +226,20 @@ "imported": [], | ||
{ | ||
"uid": "eda2-49" | ||
"uid": "dcf0-49" | ||
}, | ||
{ | ||
"uid": "eda2-45" | ||
"uid": "dcf0-45" | ||
} | ||
] | ||
}, | ||
"eda2-39": { | ||
"dcf0-39": { | ||
"id": "/packages/router/src/route.ts", | ||
"moduleParts": { | ||
"index.production.js": "eda2-40" | ||
"index.production.js": "dcf0-40" | ||
}, | ||
"imported": [ | ||
{ | ||
"uid": "eda2-29" | ||
"uid": "dcf0-29" | ||
}, | ||
{ | ||
"uid": "eda2-35" | ||
"uid": "dcf0-35" | ||
} | ||
@@ -249,10 +249,10 @@ ], | ||
{ | ||
"uid": "eda2-49" | ||
"uid": "dcf0-49" | ||
} | ||
] | ||
}, | ||
"eda2-41": { | ||
"dcf0-41": { | ||
"id": "/packages/store/build/esm/index.js", | ||
"moduleParts": { | ||
"index.production.js": "eda2-42" | ||
"index.production.js": "dcf0-42" | ||
}, | ||
@@ -262,17 +262,17 @@ "imported": [], | ||
{ | ||
"uid": "eda2-43" | ||
"uid": "dcf0-43" | ||
}, | ||
{ | ||
"uid": "eda2-47" | ||
"uid": "dcf0-47" | ||
} | ||
] | ||
}, | ||
"eda2-43": { | ||
"dcf0-43": { | ||
"id": "/packages/router/src/routeMatch.ts", | ||
"moduleParts": { | ||
"index.production.js": "eda2-44" | ||
"index.production.js": "dcf0-44" | ||
}, | ||
"imported": [ | ||
{ | ||
"uid": "eda2-41" | ||
"uid": "dcf0-41" | ||
} | ||
@@ -282,17 +282,17 @@ ], | ||
{ | ||
"uid": "eda2-49" | ||
"uid": "dcf0-49" | ||
}, | ||
{ | ||
"uid": "eda2-47" | ||
"uid": "dcf0-47" | ||
} | ||
] | ||
}, | ||
"eda2-45": { | ||
"dcf0-45": { | ||
"id": "/packages/router/src/searchParams.ts", | ||
"moduleParts": { | ||
"index.production.js": "eda2-46" | ||
"index.production.js": "dcf0-46" | ||
}, | ||
"imported": [ | ||
{ | ||
"uid": "eda2-37" | ||
"uid": "dcf0-37" | ||
} | ||
@@ -302,35 +302,35 @@ ], | ||
{ | ||
"uid": "eda2-49" | ||
"uid": "dcf0-49" | ||
}, | ||
{ | ||
"uid": "eda2-47" | ||
"uid": "dcf0-47" | ||
} | ||
] | ||
}, | ||
"eda2-47": { | ||
"dcf0-47": { | ||
"id": "/packages/router/src/router.ts", | ||
"moduleParts": { | ||
"index.production.js": "eda2-48" | ||
"index.production.js": "dcf0-48" | ||
}, | ||
"imported": [ | ||
{ | ||
"uid": "eda2-41" | ||
"uid": "dcf0-41" | ||
}, | ||
{ | ||
"uid": "eda2-29" | ||
"uid": "dcf0-29" | ||
}, | ||
{ | ||
"uid": "eda2-35" | ||
"uid": "dcf0-35" | ||
}, | ||
{ | ||
"uid": "eda2-43" | ||
"uid": "dcf0-43" | ||
}, | ||
{ | ||
"uid": "eda2-45" | ||
"uid": "dcf0-45" | ||
}, | ||
{ | ||
"uid": "eda2-33" | ||
"uid": "dcf0-33" | ||
}, | ||
{ | ||
"uid": "eda2-31" | ||
"uid": "dcf0-31" | ||
} | ||
@@ -340,47 +340,47 @@ ], | ||
{ | ||
"uid": "eda2-49" | ||
"uid": "dcf0-49" | ||
} | ||
] | ||
}, | ||
"eda2-49": { | ||
"dcf0-49": { | ||
"id": "/packages/router/src/index.ts", | ||
"moduleParts": { | ||
"index.production.js": "eda2-50" | ||
"index.production.js": "dcf0-50" | ||
}, | ||
"imported": [ | ||
{ | ||
"uid": "eda2-29" | ||
"uid": "dcf0-29" | ||
}, | ||
{ | ||
"uid": "eda2-31" | ||
"uid": "dcf0-31" | ||
}, | ||
{ | ||
"uid": "eda2-51" | ||
"uid": "dcf0-51" | ||
}, | ||
{ | ||
"uid": "eda2-52" | ||
"uid": "dcf0-52" | ||
}, | ||
{ | ||
"uid": "eda2-35" | ||
"uid": "dcf0-35" | ||
}, | ||
{ | ||
"uid": "eda2-37" | ||
"uid": "dcf0-37" | ||
}, | ||
{ | ||
"uid": "eda2-39" | ||
"uid": "dcf0-39" | ||
}, | ||
{ | ||
"uid": "eda2-53" | ||
"uid": "dcf0-53" | ||
}, | ||
{ | ||
"uid": "eda2-43" | ||
"uid": "dcf0-43" | ||
}, | ||
{ | ||
"uid": "eda2-47" | ||
"uid": "dcf0-47" | ||
}, | ||
{ | ||
"uid": "eda2-45" | ||
"uid": "dcf0-45" | ||
}, | ||
{ | ||
"uid": "eda2-33" | ||
"uid": "dcf0-33" | ||
} | ||
@@ -391,3 +391,3 @@ ], | ||
}, | ||
"eda2-51": { | ||
"dcf0-51": { | ||
"id": "/packages/router/src/frameworks.ts", | ||
@@ -398,7 +398,7 @@ "moduleParts": {}, | ||
{ | ||
"uid": "eda2-49" | ||
"uid": "dcf0-49" | ||
} | ||
] | ||
}, | ||
"eda2-52": { | ||
"dcf0-52": { | ||
"id": "/packages/router/src/link.ts", | ||
@@ -409,7 +409,7 @@ "moduleParts": {}, | ||
{ | ||
"uid": "eda2-49" | ||
"uid": "dcf0-49" | ||
} | ||
] | ||
}, | ||
"eda2-53": { | ||
"dcf0-53": { | ||
"id": "/packages/router/src/routeInfo.ts", | ||
@@ -420,3 +420,3 @@ "moduleParts": {}, | ||
{ | ||
"uid": "eda2-49" | ||
"uid": "dcf0-49" | ||
} | ||
@@ -423,0 +423,0 @@ ] |
@@ -53,5 +53,5 @@ /** | ||
type PickUnsafe<T, K> = K extends keyof T ? Pick<T, K> : never; | ||
type PickExtra<T, K> = Expand<{ | ||
type PickExtra<T, K> = { | ||
[TKey in keyof K as string extends TKey ? never : TKey extends keyof T ? never : TKey]: K[TKey]; | ||
}>; | ||
}; | ||
type PickRequired<T> = { | ||
@@ -93,8 +93,8 @@ [K in keyof T as undefined extends T[K] ? never : K]: T[K]; | ||
} | ||
type AnyRouter = Router<any, any, any>; | ||
type AnyRouter = Router<any, any>; | ||
type RegisteredRouter = Register extends { | ||
router: Router<infer TRoute, infer TRoutesInfo, infer TRouterContext>; | ||
} ? Router<TRoute, TRoutesInfo, TRouterContext> : Router; | ||
router: Router<infer TRoute, infer TRoutesInfo>; | ||
} ? Router<TRoute, TRoutesInfo> : Router; | ||
type RegisteredRoutesInfo = Register extends { | ||
router: Router<infer TRoute, infer TRoutesInfo, infer TRouterContext>; | ||
router: Router<infer TRoute, infer TRoutesInfo>; | ||
} ? TRoutesInfo : AnyRoutesInfo; | ||
@@ -121,3 +121,8 @@ interface LocationState { | ||
type FilterRoutesFn = <TRoute extends AnyRoute>(routes: TRoute[]) => TRoute[]; | ||
interface RouterOptions<TRouteTree extends AnyRootRoute, TRouterContext> { | ||
type RouterContextOptions<TRouteTree extends AnyRoute> = AnyContext extends TRouteTree['__types']['routerContext'] ? { | ||
context?: TRouteTree['__types']['routerContext']; | ||
} : { | ||
context: TRouteTree['__types']['routerContext']; | ||
}; | ||
interface RouterOptions<TRouteTree extends AnyRoute> { | ||
history?: RouterHistory; | ||
@@ -142,6 +147,6 @@ stringifySearch?: SearchSerializer; | ||
}) => void; | ||
context?: TRouterContext; | ||
loadComponent?: (component: GetFrameworkGeneric<'Component'>) => Promise<GetFrameworkGeneric<'Component'>>; | ||
onRouteChange?: () => void; | ||
fetchServerDataFn?: FetchServerDataFn; | ||
context?: TRouteTree['__types']['routerContext']; | ||
} | ||
@@ -156,9 +161,9 @@ type FetchServerDataFn = (ctx: { | ||
} | ||
interface RouterStore<TSearchObj extends AnySearchSchema = {}, TState extends LocationState = LocationState> { | ||
interface RouterStore<TRoutesInfo extends AnyRoutesInfo = AnyRoutesInfo, TState extends LocationState = LocationState> { | ||
status: 'idle' | 'pending'; | ||
latestLocation: ParsedLocation<TSearchObj, TState>; | ||
currentMatches: RouteMatch[]; | ||
currentLocation: ParsedLocation<TSearchObj, TState>; | ||
pendingMatches?: RouteMatch[]; | ||
pendingLocation?: ParsedLocation<TSearchObj, TState>; | ||
latestLocation: ParsedLocation<TRoutesInfo['fullSearchSchema'], TState>; | ||
currentMatches: RouteMatch<TRoutesInfo, TRoutesInfo['routeIntersection']>[]; | ||
currentLocation: ParsedLocation<TRoutesInfo['fullSearchSchema'], TState>; | ||
pendingMatches?: RouteMatch<TRoutesInfo, TRoutesInfo['routeIntersection']>[]; | ||
pendingLocation?: ParsedLocation<TRoutesInfo['fullSearchSchema'], TState>; | ||
lastUpdated: number; | ||
@@ -198,5 +203,4 @@ } | ||
} | ||
interface DehydratedRouter<TRouterContext = unknown> { | ||
interface DehydratedRouter { | ||
state: DehydratedRouterState; | ||
context: TRouterContext; | ||
} | ||
@@ -211,3 +215,3 @@ type MatchCache = Record<string, MatchCacheEntry>; | ||
declare const defaultFetchServerDataFn: FetchServerDataFn; | ||
declare class Router<TRouteTree extends AnyRoute = Route, TRoutesInfo extends AnyRoutesInfo = RoutesInfo<TRouteTree>, TRouterContext = unknown> { | ||
declare class Router<TRouteTree extends AnyRoute = RootRoute, TRoutesInfo extends AnyRoutesInfo = RoutesInfo<TRouteTree>> { | ||
#private; | ||
@@ -218,6 +222,6 @@ types: { | ||
}; | ||
options: PickAsRequired<RouterOptions<TRouteTree, TRouterContext>, 'stringifySearch' | 'parseSearch' | 'context'>; | ||
options: PickAsRequired<Omit<RouterOptions<TRouteTree>, 'context'>, 'stringifySearch' | 'parseSearch'> & RouterContextOptions<TRouteTree>; | ||
history: RouterHistory; | ||
basepath: string; | ||
routeTree: Route; | ||
routeTree: RootRoute; | ||
routesById: RoutesById<TRoutesInfo>; | ||
@@ -227,19 +231,22 @@ navigateTimeout: undefined | Timeout; | ||
navigationPromise: undefined | Promise<void>; | ||
store: Store<RouterStore<TRoutesInfo['fullSearchSchema']>>; | ||
store: Store<RouterStore<TRoutesInfo>>; | ||
state: RouterStore<TRoutesInfo>; | ||
startedLoadingAt: number; | ||
resolveNavigation: () => void; | ||
constructor(options?: RouterOptions<TRouteTree, TRouterContext>); | ||
constructor(options?: RouterOptions<TRouteTree>); | ||
reset: () => void; | ||
mount: () => () => void; | ||
update: <TRoute extends AnyRootRoute = AnyRootRoute, TRoutesInfo_1 extends AnyRoutesInfo = RoutesInfo<TRoute>, TRouterContext_1 = unknown>(opts?: RouterOptions<TRoute, TRouterContext_1> | undefined) => this; | ||
update: (opts?: RouterOptions<TRouteTree>) => this; | ||
buildNext: (opts: BuildNextOptions) => ParsedLocation<{}, LocationState>; | ||
cancelMatches: () => void; | ||
load: (next?: ParsedLocation) => Promise<void>; | ||
load: (opts?: { | ||
next?: ParsedLocation; | ||
}) => Promise<void>; | ||
getRoute: <TId extends keyof TRoutesInfo["routesById"]>(id: TId) => TRoutesInfo["routesById"][TId]; | ||
loadRoute: (navigateOpts?: BuildNextOptions) => Promise<RouteMatch[]>; | ||
preloadRoute: (navigateOpts?: BuildNextOptions) => Promise<RouteMatch<DefaultRoutesInfo, AnyRoute>[]>; | ||
preloadRoute: (navigateOpts?: BuildNextOptions) => Promise<AnyRouteMatch[]>; | ||
matchRoutes: (pathname: string, opts?: { | ||
strictParseParams?: boolean; | ||
}) => RouteMatch<DefaultRoutesInfo, AnyRoute>[]; | ||
loadMatches: (resolvedMatches: RouteMatch[], loaderOpts?: { | ||
}) => AnyRouteMatch[]; | ||
loadMatches: (resolvedMatches: RouteMatch[], opts?: { | ||
preload?: boolean; | ||
@@ -252,4 +259,4 @@ }) => Promise<void>; | ||
buildLink: <TFrom extends ValidFromPath<TRoutesInfo> = "/", TTo extends string = ".">({ from, to, search, params, hash, target, replace, activeOptions, preload, preloadMaxAge: userPreloadMaxAge, preloadGcMaxAge: userPreloadGcMaxAge, preloadDelay: userPreloadDelay, disabled, }: LinkOptions<TRoutesInfo, TFrom, TTo>) => LinkInfo; | ||
dehydrate: () => DehydratedRouter<TRouterContext>; | ||
hydrate: (dehydratedRouter: DehydratedRouter<TRouterContext>) => void; | ||
dehydrate: () => DehydratedRouter; | ||
hydrate: (dehydratedRouter: DehydratedRouter) => void; | ||
} | ||
@@ -259,3 +266,3 @@ | ||
routeSearch: TRoute['__types']['searchSchema']; | ||
search: Expand<TRoutesInfo['fullSearchSchema'] & TRoute['__types']['fullSearchSchema']>; | ||
search: TRoutesInfo['fullSearchSchema'] & TRoute['__types']['fullSearchSchema']; | ||
status: 'idle' | 'pending' | 'success' | 'error'; | ||
@@ -265,2 +272,4 @@ error?: unknown; | ||
} | ||
interface AnyRouteMatch extends RouteMatch<any, any> { | ||
} | ||
declare class RouteMatch<TRoutesInfo extends AnyRoutesInfo = DefaultRoutesInfo, TRoute extends AnyRoute = AnyRoute> { | ||
@@ -271,5 +280,8 @@ #private; | ||
store: Store<RouteMatchStore<TRoutesInfo, TRoute>>; | ||
state: RouteMatchStore<TRoutesInfo, TRoute>; | ||
id: string; | ||
pathname: string; | ||
params: TRoute['__types']['allParams']; | ||
routeContext: TRoute['__types']['routeContext']; | ||
context: TRoute['__types']['context']; | ||
component: GetFrameworkGeneric<'Component'>; | ||
@@ -291,2 +303,5 @@ errorComponent: GetFrameworkGeneric<'ErrorComponent'>; | ||
}); | ||
__init: (opts: { | ||
parentMatch?: RouteMatch; | ||
}) => void; | ||
cancel: () => void; | ||
@@ -299,4 +314,2 @@ load: (opts?: { | ||
}) => Promise<void>; | ||
__setParentMatch: (parentMatch?: RouteMatch) => void; | ||
__validate: () => void; | ||
} | ||
@@ -309,2 +322,3 @@ | ||
type AnySearchSchema = {}; | ||
type AnyContext = {}; | ||
interface RouteMeta { | ||
@@ -318,3 +332,5 @@ } | ||
type RouteOptionsBaseIntersection<TCustomId, TPath> = UnionToIntersection<RouteOptionsBase<TCustomId, TPath>>; | ||
type RouteOptions<TParentRoute extends AnyRoute = AnyRoute, TCustomId extends string = string, TPath extends string = string, TParentSearchSchema extends {} = {}, TSearchSchema extends AnySearchSchema = {}, TFullSearchSchema extends AnySearchSchema = TSearchSchema, TParentParams extends AnyPathParams = {}, TParams extends Record<ParsePathParams<TPath>, unknown> = Record<ParsePathParams<TPath>, string>, TAllParams extends AnyPathParams = {}> = RouteOptionsBase<TCustomId, TPath> & { | ||
interface FrameworkRouteOptions { | ||
} | ||
type RouteOptions<TParentRoute extends AnyRoute = AnyRoute, TCustomId extends string = string, TPath extends string = string, TParentSearchSchema extends {} = {}, TSearchSchema extends AnySearchSchema = {}, TFullSearchSchema extends AnySearchSchema = TSearchSchema, TParentParams extends AnyPathParams = {}, TParams extends Record<ParsePathParams<TPath>, unknown> = Record<ParsePathParams<TPath>, string>, TAllParams extends AnyPathParams = {}, TParentContext extends AnyContext = AnyContext, TAllParentContext extends AnyContext = AnyContext, TRouteContext extends AnyContext = AnyContext, TContext extends AnyContext = TRouteContext> = RouteOptionsBase<TCustomId, TPath> & FrameworkRouteOptions & { | ||
getParentRoute: () => TParentRoute; | ||
@@ -332,3 +348,3 @@ caseSensitive?: boolean; | ||
}) => Promise<void> | void; | ||
onLoad?: OnLoadFn<TFullSearchSchema, TAllParams>; | ||
onLoad?: OnLoadFn<TFullSearchSchema, TAllParams, TRouteContext, TContext>; | ||
onLoadError?: (err: any) => void; | ||
@@ -346,3 +362,12 @@ onLoaded?: (matchContext: { | ||
}) => void; | ||
meta?: RouteMeta; | ||
getContext?: (opts: { | ||
params: TAllParams; | ||
search: TFullSearchSchema; | ||
} & (TParentRoute extends undefined ? { | ||
context?: TAllParentContext; | ||
parentContext?: TParentContext; | ||
} : { | ||
context: TAllParentContext; | ||
parentContext: TParentContext; | ||
})) => TRouteContext; | ||
} & ({ | ||
@@ -367,4 +392,4 @@ parseParams?: never; | ||
}; | ||
type OnLoadFn<TFullSearchSchema extends AnySearchSchema = {}, TAllParams extends AnyPathParams = {}> = (loaderContext: LoaderContext<TFullSearchSchema, TAllParams>) => Promise<any> | void; | ||
interface LoaderContext<TFullSearchSchema extends AnySearchSchema = {}, TAllParams extends AnyPathParams = {}> { | ||
type OnLoadFn<TFullSearchSchema extends AnySearchSchema = {}, TAllParams extends AnyPathParams = {}, TContext extends AnyContext = AnyContext, TAllContext extends AnyContext = AnyContext> = (loaderContext: LoaderContext<TFullSearchSchema, TAllParams, TContext, TAllContext>) => Promise<any> | void; | ||
interface LoaderContext<TFullSearchSchema extends AnySearchSchema = {}, TAllParams extends AnyPathParams = {}, TContext extends AnyContext = AnyContext, TAllContext extends AnyContext = AnyContext> { | ||
params: TAllParams; | ||
@@ -374,8 +399,10 @@ search: TFullSearchSchema; | ||
preload: boolean; | ||
routeContext: TContext; | ||
context: TAllContext; | ||
} | ||
type UnloaderFn<TPath extends string> = (routeMatch: RouteMatch<any, Route>) => void; | ||
type SearchFilter<T, U = T> = (prev: T) => U; | ||
type ResolveId<TParentRoute, TCustomId extends string, TPath extends string> = TParentRoute extends Route<any> ? RootRouteId : TParentRoute extends { | ||
type ResolveId<TParentRoute, TCustomId extends string, TPath extends string> = TParentRoute extends { | ||
id: infer TParentId extends string; | ||
} ? RoutePrefix<TParentId, string extends TCustomId ? TPath : TCustomId> : never; | ||
} ? RoutePrefix<TParentId, string extends TCustomId ? TPath : TCustomId> : RootRouteId; | ||
type InferFullSearchSchema<TRoute> = TRoute extends { | ||
@@ -391,7 +418,7 @@ isRoot: true; | ||
} ? TFullSearchSchema : {}; | ||
type ResolveFullSearchSchema<TParentRoute, TSearchSchema> = Expand<InferFullSearchSchema<TParentRoute> & TSearchSchema>; | ||
interface AnyRoute extends Route<any, any, any, any, any, any, any, any, any, any, any> { | ||
type ResolveFullSearchSchema<TParentRoute, TSearchSchema> = InferFullSearchSchema<TParentRoute> & TSearchSchema; | ||
interface AnyRoute extends Route<any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any> { | ||
} | ||
type RouteWithRoutesInfo<TRoute> = TRoute extends Route<infer TParentRoute, infer TPath, infer TFullPath, infer TCustomId, infer TId, infer TSearchSchema, infer TFullSearchSchema, infer TParams, infer TAllParams, infer TChildren> ? Route<any, any, any, any, any, any, any, any, any, TChildren, any> : never; | ||
declare class Route<TParentRoute extends AnyRoute = AnyRoute, TPath extends string = string, TFullPath extends ResolveFullPath<TParentRoute, TPath> = ResolveFullPath<TParentRoute, TPath>, TCustomId extends string = string, TId extends ResolveId<TParentRoute, TCustomId, TPath> = ResolveId<TParentRoute, TCustomId, TPath>, TSearchSchema extends AnySearchSchema = {}, TFullSearchSchema extends AnySearchSchema = ResolveFullSearchSchema<TParentRoute, TSearchSchema>, TParams extends Record<ParsePathParams<TPath>, unknown> = Record<ParsePathParams<TPath>, string>, TAllParams extends Expand<TParentRoute['__types']['allParams'] & TParams> = Expand<TParentRoute['__types']['allParams'] & TParams>, TChildren extends unknown = unknown, TRoutesInfo extends DefaultRoutesInfo = DefaultRoutesInfo> { | ||
type MergeFromParent<T, U> = IsAny<T, U, T & U>; | ||
declare class Route<TParentRoute extends AnyRoute = AnyRoute, TPath extends string = string, TFullPath extends ResolveFullPath<TParentRoute, TPath> = ResolveFullPath<TParentRoute, TPath>, TCustomId extends string = string, TId extends ResolveId<TParentRoute, TCustomId, TPath> = ResolveId<TParentRoute, TCustomId, TPath>, TSearchSchema extends AnySearchSchema = {}, TFullSearchSchema extends AnySearchSchema = ResolveFullSearchSchema<TParentRoute, TSearchSchema>, TParams extends Record<ParsePathParams<TPath>, unknown> = Record<ParsePathParams<TPath>, string>, TAllParams extends MergeFromParent<TParentRoute['__types']['allParams'], TParams> = MergeFromParent<TParentRoute['__types']['allParams'], TParams>, TParentContext extends TParentRoute['__types']['routeContext'] = TParentRoute['__types']['routeContext'], TAllParentContext extends TParentRoute['__types']['context'] = TParentRoute['__types']['context'], TRouteContext extends AnyContext = AnyContext, TContext extends MergeFromParent<TParentRoute['__types']['context'], TRouteContext> = MergeFromParent<TParentRoute['__types']['context'], TRouteContext>, TRouterContext extends AnyContext = AnyContext, TChildren extends unknown = unknown, TRoutesInfo extends DefaultRoutesInfo = DefaultRoutesInfo> { | ||
__types: { | ||
@@ -401,2 +428,3 @@ parentRoute: TParentRoute; | ||
fullPath: TFullPath; | ||
customId: TCustomId; | ||
id: TId; | ||
@@ -407,7 +435,12 @@ searchSchema: TSearchSchema; | ||
allParams: TAllParams; | ||
parentContext: TParentContext; | ||
allParentContext: TAllParentContext; | ||
routeContext: TRouteContext; | ||
context: TContext; | ||
children: TChildren; | ||
routesInfo: TRoutesInfo; | ||
routerContext: TRouterContext; | ||
}; | ||
isRoot: TParentRoute extends Route<any> ? true : false; | ||
options: RouteOptions<TParentRoute, TCustomId, TPath, InferFullSearchSchema<TParentRoute>, TSearchSchema, Expand<InferFullSearchSchema<TParentRoute> & TSearchSchema>, TParentRoute['__types']['allParams'], TParams, TAllParams>; | ||
options: RouteOptions<TParentRoute, TCustomId, TPath, InferFullSearchSchema<TParentRoute>, TSearchSchema, InferFullSearchSchema<TParentRoute> & TSearchSchema, TParentRoute['__types']['allParams'], TParams, TAllParams, TParentContext, TAllParentContext, TRouteContext, TContext>; | ||
parentRoute: TParentRoute; | ||
@@ -419,10 +452,14 @@ id: TId; | ||
originalIndex?: number; | ||
router?: Router<TRoutesInfo['routeTree'], TRoutesInfo, unknown>; | ||
constructor(options: RouteOptions<TParentRoute, TCustomId, TPath, InferFullSearchSchema<TParentRoute>, TSearchSchema, TFullSearchSchema, TParentRoute['__types']['allParams'], TParams, TAllParams>); | ||
init: () => void; | ||
addChildren: <TNewChildren extends AnyRoute[]>(children: TNewChildren) => Route<TParentRoute, TPath, TFullPath, TCustomId, TId, TSearchSchema, TFullSearchSchema, TParams, TAllParams, TNewChildren, DefaultRoutesInfo>; | ||
router?: Router<TRoutesInfo['routeTree'], TRoutesInfo>; | ||
constructor(options: RouteOptions<TParentRoute, TCustomId, TPath, InferFullSearchSchema<TParentRoute>, TSearchSchema, TFullSearchSchema, TParentRoute['__types']['allParams'], TParams, TAllParams, TParentContext, TAllParentContext, TRouteContext, TContext>); | ||
init: (opts: { | ||
originalIndex: number; | ||
router: AnyRouter; | ||
}) => void; | ||
addChildren: <TNewChildren extends AnyRoute[]>(children: TNewChildren) => Route<TParentRoute, TPath, TFullPath, TCustomId, TId, TSearchSchema, TFullSearchSchema, TParams, TAllParams, TParentContext, TAllParentContext, TRouteContext, TContext, TRouterContext, TNewChildren, TRoutesInfo>; | ||
} | ||
type AnyRootRoute = RootRoute<any>; | ||
declare class RootRoute<TSearchSchema extends AnySearchSchema = {}> extends Route<any, '/', '/', string, RootRouteId, TSearchSchema, TSearchSchema, {}, {}> { | ||
constructor(options?: Omit<RouteOptions<AnyRoute, RootRouteId, '', {}, TSearchSchema, NoInfer<TSearchSchema>, {}, {}, {}>, 'path' | 'id' | 'getParentRoute' | 'caseSensitive'>); | ||
type AnyRootRoute = RootRoute<any, any, any>; | ||
declare class RootRoute<TSearchSchema extends AnySearchSchema = {}, TContext extends AnyContext = AnyContext, TRouterContext extends AnyContext = AnyContext> extends Route<any, '/', '/', string, RootRouteId, TSearchSchema, TSearchSchema, {}, {}, TRouterContext, TRouterContext, TContext, MergeFromParent<TRouterContext, TContext>, TRouterContext> { | ||
constructor(options?: Omit<RouteOptions<AnyRoute, RootRouteId, '', {}, TSearchSchema, NoInfer<TSearchSchema>, {}, TRouterContext, TRouterContext, TContext, NoInfer<TContext>>, 'path' | 'id' | 'getParentRoute' | 'caseSensitive'>); | ||
static withRouterContext: <TRouterContext_1 extends AnyContext>() => <TSearchSchema_1 extends AnySearchSchema = {}, TContext_1 extends AnyContext = AnyContext>(options?: Omit<RouteOptions<AnyRoute, "__root__", "", {}, TSearchSchema_1, NoInfer<TSearchSchema_1>, {}, TRouterContext_1, TRouterContext_1, TContext_1, TRouterContext_1 & TContext_1, AnyContext, AnyContext>, "caseSensitive" | "id" | "path" | "getParentRoute"> | undefined) => RootRoute<TSearchSchema_1, TContext_1, TRouterContext_1>; | ||
} | ||
@@ -436,3 +473,4 @@ type ResolveFullPath<TParentRoute extends AnyRoute, TPath extends string, TPrefixed extends RoutePrefix<TParentRoute['fullPath'], TPath> = RoutePrefix<TParentRoute['fullPath'], TPath>> = TPrefixed extends RootRouteId ? '/' : TrimPathRight<`${TPrefixed}`>; | ||
interface AnyRoutesInfo { | ||
routeTree: AnyRoute; | ||
routeTree: AnyRootRoute; | ||
routeUnion: AnyRoute; | ||
routesById: Record<string, AnyRoute>; | ||
@@ -442,2 +480,3 @@ routesByFullPath: Record<string, AnyRoute>; | ||
routePaths: any; | ||
routeIntersection: AnyRoute; | ||
fullSearchSchema: Record<string, any>; | ||
@@ -447,3 +486,4 @@ allParams: Record<string, any>; | ||
interface DefaultRoutesInfo { | ||
routeTree: Route; | ||
routeTree: RootRoute; | ||
routeUnion: AnyRoute; | ||
routesById: Record<string, Route>; | ||
@@ -453,2 +493,3 @@ routesByFullPath: Record<string, Route>; | ||
routePaths: string; | ||
routeIntersection: AnyRoute; | ||
fullSearchSchema: AnySearchSchema; | ||
@@ -459,13 +500,13 @@ allParams: AnyPathParams; | ||
} | ||
interface RoutesInfoInner<TRouteTree extends AnyRoute, TRoutes extends AnyRoute = Route, TRoutesById = { | ||
'/': TRoutes; | ||
interface RoutesInfoInner<TRouteTree extends AnyRoute, TRouteUnion extends AnyRoute = Route, TRoutesById = { | ||
'/': TRouteUnion; | ||
} & { | ||
[TRoute in TRoutes as TRoute['id']]: TRoute; | ||
[TRoute in TRouteUnion as TRoute['id']]: TRoute; | ||
}, TRoutesByFullPath = { | ||
'/': TRoutes; | ||
'/': TRouteUnion; | ||
} & { | ||
[TRoute in TRoutes as TRoute['fullPath'] extends RootRouteId ? never : string extends TRoute['fullPath'] ? never : TRoute['fullPath']]: TRoute; | ||
[TRoute in TRouteUnion as TRoute['fullPath'] extends RootRouteId ? never : string extends TRoute['fullPath'] ? never : TRoute['fullPath']]: TRoute; | ||
}> { | ||
routeTree: TRouteTree; | ||
routes: TRoutes; | ||
routeUnion: TRouteUnion; | ||
routesById: TRoutesById; | ||
@@ -475,7 +516,28 @@ routesByFullPath: TRoutesByFullPath; | ||
routePaths: keyof TRoutesByFullPath; | ||
fullSearchSchema: Partial<UnionToIntersection<TRoutes['__types']['fullSearchSchema']>>; | ||
allParams: Partial<UnionToIntersection<TRoutes['__types']['allParams']>>; | ||
routeIntersection: Route<TRouteUnion['__types']['parentRoute'], // TParentRoute, | ||
TRouteUnion['__types']['path'], // TPath, | ||
TRouteUnion['__types']['fullPath'], // TFullPath, | ||
TRouteUnion['__types']['customId'], // TCustomId, | ||
TRouteUnion['__types']['id'], // TId, | ||
// TId, | ||
UnionToIntersection<TRouteUnion['__types']['searchSchema']> & {}, // TSearchSchema, | ||
// TSearchSchema, | ||
UnionToIntersection<TRouteUnion['__types']['fullSearchSchema']> & {}, // TFullSearchSchema, | ||
UnionToIntersection<TRouteUnion['__types']['params']>, // TParams, | ||
UnionToIntersection<TRouteUnion['__types']['allParams']>, // TAllParams, | ||
UnionToIntersection<TRouteUnion['__types']['parentContext']>, // TParentContext, | ||
UnionToIntersection<TRouteUnion['__types']['allParentContext']>, // TAllParentContext, | ||
// TAllParentContext, | ||
UnionToIntersection<TRouteUnion['__types']['routerContext']> & {}, // TContext, | ||
// TContext, | ||
UnionToIntersection<TRouteUnion['__types']['context']> & {}, // TAllContext, | ||
// TAllContext, | ||
UnionToIntersection<TRouteUnion['__types']['routerContext']> & {}, // TRouterContext, | ||
TRouteUnion['__types']['children'], // TChildren, | ||
TRouteUnion['__types']['routesInfo']>; | ||
fullSearchSchema: Partial<UnionToIntersection<TRouteUnion['__types']['fullSearchSchema']>>; | ||
allParams: Partial<UnionToIntersection<TRouteUnion['__types']['allParams']>>; | ||
} | ||
type ParseRoute<TRouteTree> = TRouteTree extends AnyRoute ? TRouteTree | ParseRouteChildren<TRouteTree> : never; | ||
type ParseRouteChildren<TRouteTree> = TRouteTree extends Route<any, any, any, any, any, any, any, any, any, infer TChildren, any> ? unknown extends TChildren ? never : TChildren extends AnyRoute[] ? Values<{ | ||
type ParseRouteChildren<TRouteTree> = TRouteTree extends Route<any, any, any, any, any, any, any, any, any, any, any, any, any, any, infer TChildren, any> ? unknown extends TChildren ? never : TChildren extends AnyRoute[] ? Values<{ | ||
[TId in TChildren[number]['id']]: ParseRouteChild<TChildren[number], TId>; | ||
@@ -529,3 +591,3 @@ }> : never : never; | ||
} & CheckPath<TRoutesInfo, NoInfer<TResolvedTo>, {}> & SearchParamOptions<TRoutesInfo, TFrom, TResolvedTo> & PathParamOptions<TRoutesInfo, TFrom, TResolvedTo>; | ||
type SearchParamOptions<TRoutesInfo extends AnyRoutesInfo, TFrom, TTo, TFromSchema = Expand<UnionToIntersection<TRoutesInfo['fullSearchSchema'] & RouteByPath<TRoutesInfo, TFrom> extends never ? {} : RouteByPath<TRoutesInfo, TFrom>['__types']['fullSearchSchema']>>, TToSchema = Partial<RouteByPath<TRoutesInfo, TFrom>['__types']['fullSearchSchema']> & Omit<RouteByPath<TRoutesInfo, TTo>['__types']['fullSearchSchema'], keyof PickRequired<RouteByPath<TRoutesInfo, TFrom>['__types']['fullSearchSchema']>>, TFromFullSchema = Expand<UnionToIntersection<TRoutesInfo['fullSearchSchema'] & TFromSchema>>, TToFullSchema = Expand<UnionToIntersection<TRoutesInfo['fullSearchSchema'] & TToSchema>>> = keyof PickRequired<TToSchema> extends never ? { | ||
type SearchParamOptions<TRoutesInfo extends AnyRoutesInfo, TFrom, TTo, TFromSchema = UnionToIntersection<TRoutesInfo['fullSearchSchema'] & RouteByPath<TRoutesInfo, TFrom> extends never ? {} : RouteByPath<TRoutesInfo, TFrom>['__types']['fullSearchSchema']>, TToSchema = Partial<RouteByPath<TRoutesInfo, TFrom>['__types']['fullSearchSchema']> & Omit<RouteByPath<TRoutesInfo, TTo>['__types']['fullSearchSchema'], keyof PickRequired<RouteByPath<TRoutesInfo, TFrom>['__types']['fullSearchSchema']>>, TFromFullSchema = UnionToIntersection<TRoutesInfo['fullSearchSchema'] & TFromSchema>, TToFullSchema = UnionToIntersection<TRoutesInfo['fullSearchSchema'] & TToSchema>> = keyof PickRequired<TToSchema> extends never ? { | ||
search?: true | SearchReducer<TFromFullSchema, TToFullSchema>; | ||
@@ -538,3 +600,3 @@ } : { | ||
} | ((current: TFrom) => TTo); | ||
type PathParamOptions<TRoutesInfo extends AnyRoutesInfo, TFrom, TTo, TFromSchema = Expand<UnionToIntersection<RouteByPath<TRoutesInfo, TFrom> extends never ? {} : RouteByPath<TRoutesInfo, TFrom>['__types']['allParams']>>, TToSchema = Partial<RouteByPath<TRoutesInfo, TFrom>['__types']['allParams']> & Omit<RouteByPath<TRoutesInfo, TTo>['__types']['allParams'], keyof PickRequired<RouteByPath<TRoutesInfo, TFrom>['__types']['allParams']>>, TFromFullParams = Expand<UnionToIntersection<TRoutesInfo['allParams'] & TFromSchema>>, TToFullParams = Expand<UnionToIntersection<TRoutesInfo['allParams'] & TToSchema>>> = keyof PickRequired<TToSchema> extends never ? { | ||
type PathParamOptions<TRoutesInfo extends AnyRoutesInfo, TFrom, TTo, TFromSchema = UnionToIntersection<RouteByPath<TRoutesInfo, TFrom> extends never ? {} : RouteByPath<TRoutesInfo, TFrom>['__types']['allParams']>, TToSchema = Partial<RouteByPath<TRoutesInfo, TFrom>['__types']['allParams']> & Omit<RouteByPath<TRoutesInfo, TTo>['__types']['allParams'], keyof PickRequired<RouteByPath<TRoutesInfo, TFrom>['__types']['allParams']>>, TFromFullParams = UnionToIntersection<TRoutesInfo['allParams'] & TFromSchema>, TToFullParams = UnionToIntersection<TRoutesInfo['allParams'] & TToSchema>> = keyof PickRequired<TToSchema> extends never ? { | ||
params?: ParamsReducer<TFromFullParams, TToFullParams>; | ||
@@ -565,11 +627,11 @@ } : { | ||
type CheckPath<TRoutesInfo extends AnyRoutesInfo, TPath, TPass> = Exclude<TPath, TRoutesInfo['routePaths']> extends never ? TPass : CheckPathError<TRoutesInfo, Exclude<TPath, TRoutesInfo['routePaths']>>; | ||
type CheckPathError<TRoutesInfo extends AnyRoutesInfo, TInvalids> = Expand<{ | ||
type CheckPathError<TRoutesInfo extends AnyRoutesInfo, TInvalids> = { | ||
Error: `${TInvalids extends string ? TInvalids : never} is not a valid route path.`; | ||
'Valid Route Paths': TRoutesInfo['routePaths']; | ||
}>; | ||
}; | ||
type CheckId<TRoutesInfo extends AnyRoutesInfo, TPath, TPass> = Exclude<TPath, TRoutesInfo['routeIds']> extends never ? TPass : CheckIdError<TRoutesInfo, Exclude<TPath, TRoutesInfo['routeIds']>>; | ||
type CheckIdError<TRoutesInfo extends AnyRoutesInfo, TInvalids> = Expand<{ | ||
type CheckIdError<TRoutesInfo extends AnyRoutesInfo, TInvalids> = { | ||
Error: `${TInvalids extends string ? TInvalids : never} is not a valid route ID.`; | ||
'Valid Route IDs': TRoutesInfo['routeIds']; | ||
}>; | ||
}; | ||
type ResolveRelativePath<TFrom, TTo = '.'> = TFrom extends string ? TTo extends string ? TTo extends '.' ? TFrom : TTo extends `./` ? Join<[TFrom, '/']> : TTo extends `./${infer TRest}` ? ResolveRelativePath<TFrom, TRest> : TTo extends `/${infer TRest}` ? TTo : Split<TTo> extends ['..', ...infer ToRest] ? Split<TFrom> extends [...infer FromRest, infer FromTail] ? ToRest extends ['/'] ? Join<[...FromRest, '/']> : ResolveRelativePath<Join<FromRest>, Join<ToRest>> : never : Split<TTo> extends ['.', ...infer ToRest] ? ToRest extends ['/'] ? Join<[TFrom, '/']> : ResolveRelativePath<TFrom, Join<ToRest>> : CleanPath<Join<['/', ...Split<TFrom>, ...Split<TTo>]>> : never : never; | ||
@@ -601,2 +663,2 @@ type ValidFromPath<TRoutesInfo extends AnyRoutesInfo = DefaultRoutesInfo> = undefined | (string extends TRoutesInfo['routePaths'] ? string : TRoutesInfo['routePaths']); | ||
export { ActiveOptions, AnyLoaderData, AnyPathParams, AnyRootRoute, AnyRoute, AnyRouter, AnyRoutesInfo, AnySearchSchema, BuildNextOptions, CheckId, CheckIdError, CheckPath, CheckPathError, CheckRelativePath, DeepAwaited, DefaultRoutesInfo, DefinedPathParamWarning, DehydratedRouter, DehydratedRouterState, Expand, FilterRoutesFn, FrameworkGenerics, FromLocation, GetFrameworkGeneric, InferFullSearchSchema, IsAny, IsAnyBoolean, IsKnown, LinkInfo, LinkOptions, ListenerFn, LoaderContext, LoaderState, LocationState, MatchCache, MatchCacheEntry, MatchLocation, MatchRouteOptions, NavigateOptions, NoInfer, OnLoadFn, ParentParams, ParsePathParams, ParseRoute, ParseRouteChild, ParseRouteChildren, ParsedLocation, ParsedPath, PathParamMask, PathParamOptions, PickAsPartial, PickAsRequired, PickExclude, PickExtra, PickExtract, PickRequired, PickUnsafe, Register, RegisteredRouter, RegisteredRoutesInfo, RelativeToPathAutoComplete, ResolveFullSearchSchema, ResolveRelativePath, RootRoute, RootRouteId, Route, RouteById, RouteByPath, RouteMatch, RouteMatchStore, RouteMeta, RouteOptions, RouteOptionsBase, RouteOptionsBaseIntersection, RouteWithRoutesInfo, Router, RouterContext, RouterHistory, RouterLocation, RouterOptions, RouterStore, RoutesById, RoutesInfo, RoutesInfoInner, SearchFilter, SearchParamOptions, SearchParser, SearchSchemaValidator, SearchSchemaValidatorFn, SearchSchemaValidatorObj, SearchSerializer, Segment, Split, Timeout, ToIdOption, ToOptions, ToPathOption, UnionToIntersection, UnloaderFn, Updater, ValidFromPath, ValueKeys, Values, cleanPath, createBrowserHistory, createHashHistory, createMemoryHistory, decode, defaultFetchServerDataFn, defaultParseSearch, defaultStringifySearch, encode, functionalUpdate, interpolatePath, isPlainObject, joinPaths, last, matchByPath, matchPathname, parsePathname, parseSearchWith, pick, replaceEqualDeep, resolvePath, rootRouteId, stringifySearchWith, trimPath, trimPathLeft, trimPathRight, warning }; | ||
export { ActiveOptions, AnyContext, AnyLoaderData, AnyPathParams, AnyRootRoute, AnyRoute, AnyRouteMatch, AnyRouter, AnyRoutesInfo, AnySearchSchema, BuildNextOptions, CheckId, CheckIdError, CheckPath, CheckPathError, CheckRelativePath, DeepAwaited, DefaultRoutesInfo, DefinedPathParamWarning, DehydratedRouter, DehydratedRouterState, Expand, FilterRoutesFn, FrameworkGenerics, FrameworkRouteOptions, FromLocation, GetFrameworkGeneric, InferFullSearchSchema, IsAny, IsAnyBoolean, IsKnown, LinkInfo, LinkOptions, ListenerFn, LoaderContext, LoaderState, LocationState, MatchCache, MatchCacheEntry, MatchLocation, MatchRouteOptions, NavigateOptions, NoInfer, OnLoadFn, ParentParams, ParsePathParams, ParseRoute, ParseRouteChild, ParseRouteChildren, ParsedLocation, ParsedPath, PathParamMask, PathParamOptions, PickAsPartial, PickAsRequired, PickExclude, PickExtra, PickExtract, PickRequired, PickUnsafe, Register, RegisteredRouter, RegisteredRoutesInfo, RelativeToPathAutoComplete, ResolveFullSearchSchema, ResolveRelativePath, RootRoute, RootRouteId, Route, RouteById, RouteByPath, RouteMatch, RouteMatchStore, RouteMeta, RouteOptions, RouteOptionsBase, RouteOptionsBaseIntersection, Router, RouterContext, RouterHistory, RouterLocation, RouterOptions, RouterStore, RoutesById, RoutesInfo, RoutesInfoInner, SearchFilter, SearchParamOptions, SearchParser, SearchSchemaValidator, SearchSchemaValidatorFn, SearchSchemaValidatorObj, SearchSerializer, Segment, Split, Timeout, ToIdOption, ToOptions, ToPathOption, UnionToIntersection, UnloaderFn, Updater, ValidFromPath, ValueKeys, Values, cleanPath, createBrowserHistory, createHashHistory, createMemoryHistory, decode, defaultFetchServerDataFn, defaultParseSearch, defaultStringifySearch, encode, functionalUpdate, interpolatePath, isPlainObject, joinPaths, last, matchByPath, matchPathname, parsePathname, parseSearchWith, pick, replaceEqualDeep, resolvePath, rootRouteId, stringifySearchWith, trimPath, trimPathLeft, trimPathRight, warning }; |
@@ -485,3 +485,5 @@ /** | ||
} | ||
init = () => { | ||
init = opts => { | ||
this.originalIndex = opts.originalIndex; | ||
this.router = opts.router; | ||
const allOptions = this.options; | ||
@@ -534,2 +536,5 @@ const isRoot = !allOptions?.path && !allOptions?.id; | ||
} | ||
static withRouterContext = () => { | ||
return options => new RootRoute(options); | ||
}; | ||
} | ||
@@ -611,5 +616,5 @@ | ||
if (this.state === previous) return; | ||
this.options?.onUpdate?.(this.state, previous); | ||
this.queue.push(() => { | ||
this.listeners.forEach(listener => listener(this.state, previous)); | ||
this.options?.onUpdate?.(this.state, previous); | ||
}); | ||
@@ -649,4 +654,9 @@ this.#flush(); | ||
status: 'idle' | ||
}, { | ||
onUpdate: next => { | ||
this.state = next; | ||
} | ||
}) | ||
}); | ||
this.state = this.store.state; | ||
if (!this.#hasLoaders()) { | ||
@@ -659,2 +669,56 @@ this.store.setState(s => ({ | ||
} | ||
#hasLoaders = () => { | ||
return !!(this.route.options.onLoad || componentTypes.some(d => this.route.options[d]?.preload)); | ||
}; | ||
__init = opts => { | ||
// Validate the search params and stabilize them | ||
this.parentMatch = opts.parentMatch; | ||
const parentSearch = this.parentMatch?.state.search ?? this.router.state.latestLocation.search; | ||
try { | ||
const validator = typeof this.route.options.validateSearch === 'object' ? this.route.options.validateSearch.parse : this.route.options.validateSearch; | ||
let nextSearch = validator?.(parentSearch) ?? {}; | ||
this.store.setState(s => ({ | ||
...s, | ||
routeSearch: nextSearch, | ||
search: { | ||
...parentSearch, | ||
...nextSearch | ||
} | ||
})); | ||
componentTypes.map(async type => { | ||
const component = this.route.options[type]; | ||
if (typeof this[type] !== 'function') { | ||
this[type] = component; | ||
} | ||
}); | ||
const parent = this.parentMatch; | ||
this.routeContext = this.route.options.getContext?.({ | ||
parentContext: parent?.routeContext, | ||
context: parent?.context, | ||
params: this.params, | ||
search: this.state.search | ||
}) || {}; | ||
this.context = parent ? { | ||
...parent.context, | ||
...this.routeContext | ||
} : { | ||
...this.router?.options.context, | ||
...this.routeContext | ||
}; | ||
} catch (err) { | ||
console.error(err); | ||
const error = new Error('Invalid search params found', { | ||
cause: err | ||
}); | ||
error.code = 'INVALID_SEARCH_PARAMS'; | ||
this.store.setState(s => ({ | ||
...s, | ||
status: 'error', | ||
error: error | ||
})); | ||
// Do not proceed with loading the route | ||
return; | ||
} | ||
}; | ||
cancel = () => { | ||
@@ -665,3 +729,3 @@ this.abortController?.abort(); | ||
// If the match is invalid, errored or idle, trigger it to load | ||
if (this.store.state.status !== 'pending') { | ||
if (this.state.status !== 'pending') { | ||
await this.fetch(opts); | ||
@@ -683,3 +747,3 @@ } | ||
// as loading or resolved | ||
if (this.store.state.status === 'idle') { | ||
if (this.state.status === 'idle') { | ||
this.store.setState(s => ({ | ||
@@ -706,5 +770,7 @@ ...s, | ||
params: this.params, | ||
search: this.store.state.search, | ||
search: this.state.search, | ||
signal: this.abortController.signal, | ||
preload: !!opts?.preload | ||
preload: !!opts?.preload, | ||
routeContext: this.routeContext, | ||
context: this.context | ||
}); | ||
@@ -737,46 +803,2 @@ } | ||
}; | ||
#hasLoaders = () => { | ||
return !!(this.route.options.onLoad || componentTypes.some(d => this.route.options[d]?.preload)); | ||
}; | ||
__setParentMatch = parentMatch => { | ||
if (!this.parentMatch && parentMatch) { | ||
this.parentMatch = parentMatch; | ||
} | ||
}; | ||
__validate = () => { | ||
// Validate the search params and stabilize them | ||
const parentSearch = this.parentMatch?.store.state.search ?? this.router.store.state.latestLocation.search; | ||
try { | ||
const validator = typeof this.route.options.validateSearch === 'object' ? this.route.options.validateSearch.parse : this.route.options.validateSearch; | ||
let nextSearch = validator?.(parentSearch) ?? {}; | ||
this.store.setState(s => ({ | ||
...s, | ||
routeSearch: nextSearch, | ||
search: { | ||
...parentSearch, | ||
...nextSearch | ||
} | ||
})); | ||
componentTypes.map(async type => { | ||
const component = this.route.options[type]; | ||
if (typeof this[type] !== 'function') { | ||
this[type] = component; | ||
} | ||
}); | ||
} catch (err) { | ||
console.error(err); | ||
const error = new Error('Invalid search params found', { | ||
cause: err | ||
}); | ||
error.code = 'INVALID_SEARCH_PARAMS'; | ||
this.store.setState(s => ({ | ||
...s, | ||
status: 'error', | ||
error: error | ||
})); | ||
// Do not proceed with loading the route | ||
return; | ||
} | ||
}; | ||
} | ||
@@ -866,3 +888,8 @@ | ||
}; | ||
this.store = new Store(getInitialRouterState()); | ||
this.store = new Store(getInitialRouterState(), { | ||
onUpdate: state => { | ||
this.state = state; | ||
} | ||
}); | ||
this.state = this.store.state; | ||
this.basepath = ''; | ||
@@ -881,3 +908,3 @@ this.update(options); | ||
// If the router matches are empty, load the matches | ||
if (!this.store.state.currentMatches.length) { | ||
if (!this.state.currentMatches.length) { | ||
this.load(); | ||
@@ -921,3 +948,5 @@ } | ||
this.#unsubHistory = this.history.listen(() => { | ||
this.load(this.#parseLocation(this.store.state.latestLocation)); | ||
this.load({ | ||
next: this.#parseLocation(this.state.latestLocation) | ||
}); | ||
}); | ||
@@ -948,7 +977,7 @@ } | ||
cancelMatches = () => { | ||
[...this.store.state.currentMatches, ...(this.store.state.pendingMatches || [])].forEach(match => { | ||
[...this.state.currentMatches, ...(this.state.pendingMatches || [])].forEach(match => { | ||
match.cancel(); | ||
}); | ||
}; | ||
load = async next => { | ||
load = async opts => { | ||
let now = Date.now(); | ||
@@ -962,7 +991,7 @@ const startedAt = now; | ||
this.store.batch(() => { | ||
if (next) { | ||
if (opts?.next) { | ||
// Ingest the new location | ||
this.store.setState(s => ({ | ||
...s, | ||
latestLocation: next | ||
latestLocation: opts.next | ||
})); | ||
@@ -972,3 +1001,3 @@ } | ||
// Match the routes | ||
matches = this.matchRoutes(this.store.state.latestLocation.pathname, { | ||
matches = this.matchRoutes(this.state.latestLocation.pathname, { | ||
strictParseParams: true | ||
@@ -980,3 +1009,3 @@ }); | ||
pendingMatches: matches, | ||
pendingLocation: this.store.state.latestLocation | ||
pendingLocation: this.state.latestLocation | ||
})); | ||
@@ -987,3 +1016,5 @@ }); | ||
try { | ||
await this.loadMatches(matches); | ||
await this.loadMatches(matches | ||
// opts | ||
); | ||
} catch (err) { | ||
@@ -997,3 +1028,3 @@ console.warn(err); | ||
} | ||
const previousMatches = this.store.state.currentMatches; | ||
const previousMatches = this.state.currentMatches; | ||
const exiting = [], | ||
@@ -1015,7 +1046,7 @@ staying = []; | ||
params: d.params, | ||
search: d.store.state.routeSearch | ||
search: d.state.routeSearch | ||
}); | ||
// Clear non-loading error states when match leaves | ||
if (d.store.state.status === 'error') { | ||
if (d.state.status === 'error') { | ||
this.store.setState(s => ({ | ||
@@ -1031,3 +1062,3 @@ ...s, | ||
params: d.params, | ||
search: d.store.state.routeSearch | ||
search: d.state.routeSearch | ||
}); | ||
@@ -1038,11 +1069,9 @@ }); | ||
params: d.params, | ||
search: d.store.state.search | ||
search: d.state.search | ||
}); | ||
// delete this.store.state.matchCache[d.id] // TODO: | ||
}); | ||
this.store.setState(s => ({ | ||
...s, | ||
status: 'idle', | ||
currentLocation: this.store.state.latestLocation, | ||
currentLocation: this.state.latestLocation, | ||
currentMatches: matches, | ||
@@ -1060,3 +1089,3 @@ pendingLocation: undefined, | ||
}; | ||
loadRoute = async (navigateOpts = this.store.state.latestLocation) => { | ||
loadRoute = async (navigateOpts = this.state.latestLocation) => { | ||
const next = this.buildNext(navigateOpts); | ||
@@ -1069,3 +1098,3 @@ const matches = this.matchRoutes(next.pathname, { | ||
}; | ||
preloadRoute = async (navigateOpts = this.store.state.latestLocation) => { | ||
preloadRoute = async (navigateOpts = this.state.latestLocation) => { | ||
const next = this.buildNext(navigateOpts); | ||
@@ -1085,3 +1114,3 @@ const matches = this.matchRoutes(next.pathname, { | ||
} | ||
const existingMatches = [...this.store.state.currentMatches, ...(this.store.state.pendingMatches ?? [])]; | ||
const existingMatches = [...this.state.currentMatches, ...(this.state.pendingMatches ?? [])]; | ||
const findInRouteTree = async routes => { | ||
@@ -1132,5 +1161,3 @@ const parentMatch = last(matches); | ||
const matchId = interpolatePath(foundRoute.id, params, true); | ||
const match = existingMatches.find(d => d.id === matchId) || | ||
// this.store.state.matchCache[matchId]?.match || // TODO: | ||
new RouteMatch(this, foundRoute, { | ||
const match = existingMatches.find(d => d.id === matchId) || new RouteMatch(this, foundRoute, { | ||
id: matchId, | ||
@@ -1151,11 +1178,5 @@ params, | ||
}; | ||
loadMatches = async (resolvedMatches, loaderOpts) => { | ||
linkMatches(resolvedMatches); | ||
loadMatches = async (resolvedMatches, opts) => { | ||
initMatches(resolvedMatches); | ||
// this.cleanMatchCache() | ||
resolvedMatches.forEach(async match => { | ||
// Validate the match (loads search params etc) | ||
match.__validate(); | ||
}); | ||
// Check each match middleware to see if the route can be accessed | ||
@@ -1169,3 +1190,3 @@ await Promise.all(resolvedMatches.map(async match => { | ||
} catch (err) { | ||
if (!loaderOpts?.preload) { | ||
if (!opts?.preload) { | ||
match.route.options.onLoadError?.(err); | ||
@@ -1178,10 +1199,12 @@ } | ||
const prevMatch = resolvedMatches[1]; | ||
const search = match.store.state.search; | ||
if (search.__data?.matchId && search.__data.matchId !== match.id) { | ||
return; | ||
} | ||
match.state.search; | ||
// if (opts?.filter && !opts.filter(match)) { | ||
// return | ||
// } | ||
match.load({ | ||
preload: loaderOpts?.preload | ||
preload: opts?.preload | ||
}); | ||
if (match.store.state.status !== 'success' && match.__loadPromise) { | ||
if (match.state.status !== 'success' && match.__loadPromise) { | ||
// Wait for the first sign of activity from the match | ||
@@ -1243,6 +1266,6 @@ await match.__loadPromise; | ||
if (opts?.pending) { | ||
if (!this.store.state.pendingLocation) { | ||
if (!this.state.pendingLocation) { | ||
return false; | ||
} | ||
return matchPathname(this.basepath, this.store.state.pendingLocation.pathname, { | ||
return matchPathname(this.basepath, this.state.pendingLocation.pathname, { | ||
...opts, | ||
@@ -1252,3 +1275,3 @@ to: next.pathname | ||
} | ||
return matchPathname(this.basepath, this.store.state.currentLocation.pathname, { | ||
return matchPathname(this.basepath, this.state.currentLocation.pathname, { | ||
...opts, | ||
@@ -1299,7 +1322,7 @@ to: next.pathname | ||
// Compare path/hash for matches | ||
const pathIsEqual = this.store.state.currentLocation.pathname === next.pathname; | ||
const currentPathSplit = this.store.state.currentLocation.pathname.split('/'); | ||
const pathIsEqual = this.state.currentLocation.pathname === next.pathname; | ||
const currentPathSplit = this.state.currentLocation.pathname.split('/'); | ||
const nextPathSplit = next.pathname.split('/'); | ||
const pathIsFuzzyEqual = nextPathSplit.every((d, i) => d === currentPathSplit[i]); | ||
const hashIsEqual = this.store.state.currentLocation.hash === next.hash; | ||
const hashIsEqual = this.state.currentLocation.hash === next.hash; | ||
// Combine the matches based on user options | ||
@@ -1367,11 +1390,10 @@ const pathTest = activeOptions?.exact ? pathIsEqual : pathIsFuzzyEqual; | ||
state: { | ||
...pick(this.store.state, ['latestLocation', 'currentLocation', 'status', 'lastUpdated']), | ||
currentMatches: this.store.state.currentMatches.map(match => ({ | ||
...pick(this.state, ['latestLocation', 'currentLocation', 'status', 'lastUpdated']), | ||
currentMatches: this.state.currentMatches.map(match => ({ | ||
id: match.id, | ||
state: { | ||
...pick(match.store.state, ['status']) | ||
status: match.state.status | ||
} | ||
})) | ||
}, | ||
context: this.options.context | ||
} | ||
}; | ||
@@ -1381,4 +1403,2 @@ }; | ||
this.store.setState(s => { | ||
this.options.context = dehydratedRouter.context; | ||
// Match the routes | ||
@@ -1396,3 +1416,3 @@ const currentMatches = this.matchRoutes(dehydratedRouter.state.latestLocation.pathname, { | ||
}); | ||
currentMatches.forEach(match => match.__validate()); | ||
initMatches(currentMatches); | ||
return { | ||
@@ -1408,5 +1428,6 @@ ...s, | ||
routes.forEach((route, i) => { | ||
route.init(); | ||
route.originalIndex = i; | ||
route.router = this; | ||
route.init({ | ||
originalIndex: i, | ||
router: this | ||
}); | ||
const existingRoute = this.routesById[route.id]; | ||
@@ -1449,5 +1470,5 @@ if (existingRoute) { | ||
#buildLocation = (dest = {}) => { | ||
const fromPathname = dest.fromCurrent ? this.store.state.latestLocation.pathname : dest.from ?? this.store.state.latestLocation.pathname; | ||
const fromPathname = dest.fromCurrent ? this.state.latestLocation.pathname : dest.from ?? this.state.latestLocation.pathname; | ||
let pathname = resolvePath(this.basepath ?? '/', fromPathname, `${dest.to ?? '.'}`); | ||
const fromMatches = this.matchRoutes(this.store.state.latestLocation.pathname, { | ||
const fromMatches = this.matchRoutes(this.state.latestLocation.pathname, { | ||
strictParseParams: true | ||
@@ -1468,3 +1489,3 @@ }); | ||
// Pre filters first | ||
const preFilteredSearch = dest.__preSearchFilters?.length ? dest.__preSearchFilters?.reduce((prev, next) => next(prev), this.store.state.latestLocation.search) : this.store.state.latestLocation.search; | ||
const preFilteredSearch = dest.__preSearchFilters?.length ? dest.__preSearchFilters?.reduce((prev, next) => next(prev), this.state.latestLocation.search) : this.state.latestLocation.search; | ||
@@ -1479,5 +1500,5 @@ // Then the link/navigate function | ||
const postFilteredSearch = dest.__postSearchFilters?.length ? dest.__postSearchFilters.reduce((prev, next) => next(prev), destSearch) : destSearch; | ||
const search = replaceEqualDeep(this.store.state.latestLocation.search, postFilteredSearch); | ||
const search = replaceEqualDeep(this.state.latestLocation.search, postFilteredSearch); | ||
const searchStr = this.options.stringifySearch(search); | ||
let hash = dest.hash === true ? this.store.state.latestLocation.hash : functionalUpdate(dest.hash, this.store.state.latestLocation.hash); | ||
let hash = dest.hash === true ? this.state.latestLocation.hash : functionalUpdate(dest.hash, this.state.latestLocation.hash); | ||
hash = hash ? `#${hash}` : ''; | ||
@@ -1488,3 +1509,3 @@ return { | ||
searchStr, | ||
state: this.store.state.latestLocation.state, | ||
state: this.state.latestLocation.state, | ||
hash, | ||
@@ -1503,3 +1524,3 @@ href: `${pathname}${searchStr}${hash}`, | ||
} | ||
const isSameUrl = this.store.state.latestLocation.href === next.href; | ||
const isSameUrl = this.state.latestLocation.href === next.href; | ||
if (isSameUrl && !next.key) { | ||
@@ -1514,3 +1535,3 @@ nextAction = 'replace'; | ||
// this.load(this.#parseLocation(this.store.state.latestLocation)) | ||
// this.load(this.#parseLocation(this.state.latestLocation)) | ||
@@ -1541,8 +1562,8 @@ return this.navigationPromise = new Promise(resolve => { | ||
} | ||
function linkMatches(matches) { | ||
function initMatches(matches) { | ||
matches.forEach((match, index) => { | ||
const parent = matches[index - 1]; | ||
if (parent) { | ||
match.__setParentMatch(parent); | ||
} | ||
const parentMatch = matches[index - 1]; | ||
match.__init({ | ||
parentMatch | ||
}); | ||
}); | ||
@@ -1549,0 +1570,0 @@ } |
@@ -11,3 +11,3 @@ /** | ||
*/ | ||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).RouterCore={})}(this,(function(t){"use strict";function e(t,e){if(!t)throw new Error("Invariant failed")}const s="popstate";function a(t){let e=t.getLocation(),s=()=>{},a=new Set;const o=()=>{e=t.getLocation(),a.forEach((t=>t()))};return{get location(){return e},listen:e=>(0===a.size&&(s=t.listener(o)),a.add(e),()=>{a.delete(e),0===a.size&&s()}),push:(e,s)=>{t.pushState(e,s),o()},replace:(e,s)=>{t.replaceState(e,s),o()},go:e=>{t.go(e),o()},back:()=>{t.back(),o()},forward:()=>{t.forward(),o()}}}function o(t){const e=t?.getHref??(()=>`${window.location.pathname}${window.location.hash}${window.location.search}`),o=t?.createHref??(t=>t);return a({getLocation:()=>i(e(),history.state),listener:t=>(window.addEventListener(s,t),()=>{window.removeEventListener(s,t)}),pushState:(t,e)=>{window.history.pushState({...e,key:n()},"",o(t))},replaceState:(t,e)=>{window.history.replaceState({...e,key:n()},"",o(t))},back:()=>window.history.back(),forward:()=>window.history.forward(),go:t=>window.history.go(t)})}function r(t={initialEntries:["/"]}){const e=t.initialEntries;let s=t.initialIndex??e.length-1,o={};return a({getLocation:()=>i(e[s],o),listener:()=>()=>{},pushState:(t,a)=>{o={...a,key:n()},e.push(t),s++},replaceState:(t,a)=>{o={...a,key:n()},e[s]=t},back:()=>{s--},forward:()=>{s=Math.min(s+1,e.length-1)},go:t=>window.history.go(t)})}function i(t,e){let s=t.indexOf("#"),a=t.indexOf("?");return{href:t,pathname:t.substring(0,s>0?a>0?Math.min(s,a):s:a>0?a:t.length),hash:s>-1?t.substring(s,a):"",search:a>-1?t.substring(a):"",state:e}}function n(){return(Math.random()+1).toString(36).substring(7)}function h(t){return t[t.length-1]}function c(t,e){return"function"==typeof t?t(e):t}function u(t,e){return e.reduce(((e,s)=>(e[s]=t[s],e)),{})}function l(t,e){if(t===e)return t;const s=e,a=Array.isArray(t)&&Array.isArray(s);if(a||p(t)&&p(s)){const e=a?t.length:Object.keys(t).length,o=a?s:Object.keys(s),r=o.length,i=a?[]:{};let n=0;for(let e=0;e<r;e++){const r=a?e:o[e];i[r]=l(t[r],s[r]),i[r]===t[r]&&n++}return e===r&&n===e?t:i}return s}function p(t){if(!d(t))return!1;const e=t.constructor;if(void 0===e)return!0;const s=e.prototype;return!!d(s)&&!!s.hasOwnProperty("isPrototypeOf")}function d(t){return"[object Object]"===Object.prototype.toString.call(t)}function f(t){return m(t.filter(Boolean).join("/"))}function m(t){return t.replace(/\/{2,}/g,"/")}function g(t){return"/"===t?t:t.replace(/^\/{1,}/,"")}function y(t){return"/"===t?t:t.replace(/\/{1,}$/,"")}function w(t){return y(g(t))}function v(t,e,s){e=e.replace(new RegExp(`^${t}`),"/"),s=s.replace(new RegExp(`^${t}`),"/");let a=b(e);const o=b(s);o.forEach(((t,e)=>{if("/"===t.value)e?e===o.length-1&&a.push(t):a=[t];else if(".."===t.value)a.length>1&&"/"===h(a)?.value&&a.pop(),a.pop();else{if("."===t.value)return;a.push(t)}}));return m(f([t,...a.map((t=>t.value))]))}function b(t){if(!t)return[];const e=[];if("/"===(t=m(t)).slice(0,1)&&(t=t.substring(1),e.push({type:"pathname",value:"/"})),!t)return e;const s=t.split("/").filter(Boolean);return e.push(...s.map((t=>t.startsWith("*")?{type:"wildcard",value:t}:"$"===t.charAt(0)?{type:"param",value:t}:{type:"pathname",value:t}))),"/"===t.slice(-1)&&(t=t.substring(1),e.push({type:"pathname",value:"/"})),e}function L(t,e,s){return f(b(t).map((t=>"*"!==t.value||s?"param"===t.type?e[t.value.substring(1)]??"":t.value:"")))}function S(t,e,s){const a=_(t,e,s);if(!s.to||a)return a??{}}function _(t,e,s){if(!e.startsWith(t))return;const a=b(e="/"!=t?e.substring(t.length):e),o=b(`${s.to??"*"}`),r={};return(()=>{for(let t=0;t<Math.max(a.length,o.length);t++){const e=a[t],i=o[t],n=t===o.length-1,h=t===a.length-1;if(i){if("wildcard"===i.type)return!!e?.value&&(r["*"]=f(a.slice(t).map((t=>t.value))),!0);if("pathname"===i.type){if("/"===i.value&&!e?.value)return!0;if(e)if(s.caseSensitive){if(i.value!==e.value)return!1}else if(i.value.toLowerCase()!==e.value.toLowerCase())return!1}if(!e)return!1;if("param"===i.type){if("/"===e?.value)return!1;"$"!==e.value.charAt(0)&&(r[i.value.substring(1)]=e.value)}}if(n&&!h)return!!s.fuzzy}return!0})()?r:void 0}function P(t,e){var s,a,o,r="";for(s in t)if(void 0!==(o=t[s]))if(Array.isArray(o))for(a=0;a<o.length;a++)r&&(r+="&"),r+=encodeURIComponent(s)+"="+encodeURIComponent(o[a]);else r&&(r+="&"),r+=encodeURIComponent(s)+"="+encodeURIComponent(o);return(e||"")+r}function E(t){if(!t)return"";var e=decodeURIComponent(t);return"false"!==e&&("true"===e||("0"===e.charAt(0)?e:0*+e==0?+e:e))}function R(t){for(var e,s,a={},o=t.split("&");e=o.shift();)void 0!==a[s=(e=e.split("=")).shift()]?a[s]=[].concat(a[s],E(e.shift())):a[s]=E(e.shift());return a}const M="__root__";class x{constructor(t){this.options=t||{},this.isRoot=!t?.getParentRoute}init=()=>{const t=this.options,s=!t?.path&&!t?.id,a=this.options?.getParentRoute?.();s?this.path=M:e(a);let o=s?M:t.path;o&&"/"!==o&&(o=w(o));const r=t?.id||o;let i=s?M:f([a.id===M?"":a.id,r]);o===M&&(o="/"),i!==M&&(i=f(["/",i]));const n=i===M?"/":y(f([a.fullPath,o]));this.path=o,this.id=i,this.fullPath=n};addChildren=t=>(this.children=t,this)} | ||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).RouterCore={})}(this,(function(t){"use strict";function e(t,e){if(!t)throw new Error("Invariant failed")}const s="popstate";function a(t){let e=t.getLocation(),s=()=>{},a=new Set;const o=()=>{e=t.getLocation(),a.forEach((t=>t()))};return{get location(){return e},listen:e=>(0===a.size&&(s=t.listener(o)),a.add(e),()=>{a.delete(e),0===a.size&&s()}),push:(e,s)=>{t.pushState(e,s),o()},replace:(e,s)=>{t.replaceState(e,s),o()},go:e=>{t.go(e),o()},back:()=>{t.back(),o()},forward:()=>{t.forward(),o()}}}function o(t){const e=t?.getHref??(()=>`${window.location.pathname}${window.location.hash}${window.location.search}`),o=t?.createHref??(t=>t);return a({getLocation:()=>i(e(),history.state),listener:t=>(window.addEventListener(s,t),()=>{window.removeEventListener(s,t)}),pushState:(t,e)=>{window.history.pushState({...e,key:n()},"",o(t))},replaceState:(t,e)=>{window.history.replaceState({...e,key:n()},"",o(t))},back:()=>window.history.back(),forward:()=>window.history.forward(),go:t=>window.history.go(t)})}function r(t={initialEntries:["/"]}){const e=t.initialEntries;let s=t.initialIndex??e.length-1,o={};return a({getLocation:()=>i(e[s],o),listener:()=>()=>{},pushState:(t,a)=>{o={...a,key:n()},e.push(t),s++},replaceState:(t,a)=>{o={...a,key:n()},e[s]=t},back:()=>{s--},forward:()=>{s=Math.min(s+1,e.length-1)},go:t=>window.history.go(t)})}function i(t,e){let s=t.indexOf("#"),a=t.indexOf("?");return{href:t,pathname:t.substring(0,s>0?a>0?Math.min(s,a):s:a>0?a:t.length),hash:s>-1?t.substring(s,a):"",search:a>-1?t.substring(a):"",state:e}}function n(){return(Math.random()+1).toString(36).substring(7)}function h(t){return t[t.length-1]}function c(t,e){return"function"==typeof t?t(e):t}function u(t,e){return e.reduce(((e,s)=>(e[s]=t[s],e)),{})}function l(t,e){if(t===e)return t;const s=e,a=Array.isArray(t)&&Array.isArray(s);if(a||p(t)&&p(s)){const e=a?t.length:Object.keys(t).length,o=a?s:Object.keys(s),r=o.length,i=a?[]:{};let n=0;for(let e=0;e<r;e++){const r=a?e:o[e];i[r]=l(t[r],s[r]),i[r]===t[r]&&n++}return e===r&&n===e?t:i}return s}function p(t){if(!d(t))return!1;const e=t.constructor;if(void 0===e)return!0;const s=e.prototype;return!!d(s)&&!!s.hasOwnProperty("isPrototypeOf")}function d(t){return"[object Object]"===Object.prototype.toString.call(t)}function f(t){return m(t.filter(Boolean).join("/"))}function m(t){return t.replace(/\/{2,}/g,"/")}function g(t){return"/"===t?t:t.replace(/^\/{1,}/,"")}function y(t){return"/"===t?t:t.replace(/\/{1,}$/,"")}function w(t){return y(g(t))}function v(t,e,s){e=e.replace(new RegExp(`^${t}`),"/"),s=s.replace(new RegExp(`^${t}`),"/");let a=b(e);const o=b(s);o.forEach(((t,e)=>{if("/"===t.value)e?e===o.length-1&&a.push(t):a=[t];else if(".."===t.value)a.length>1&&"/"===h(a)?.value&&a.pop(),a.pop();else{if("."===t.value)return;a.push(t)}}));return m(f([t,...a.map((t=>t.value))]))}function b(t){if(!t)return[];const e=[];if("/"===(t=m(t)).slice(0,1)&&(t=t.substring(1),e.push({type:"pathname",value:"/"})),!t)return e;const s=t.split("/").filter(Boolean);return e.push(...s.map((t=>t.startsWith("*")?{type:"wildcard",value:t}:"$"===t.charAt(0)?{type:"param",value:t}:{type:"pathname",value:t}))),"/"===t.slice(-1)&&(t=t.substring(1),e.push({type:"pathname",value:"/"})),e}function L(t,e,s){return f(b(t).map((t=>"*"!==t.value||s?"param"===t.type?e[t.value.substring(1)]??"":t.value:"")))}function S(t,e,s){const a=P(t,e,s);if(!s.to||a)return a??{}}function P(t,e,s){if(!e.startsWith(t))return;const a=b(e="/"!=t?e.substring(t.length):e),o=b(`${s.to??"*"}`),r={};return(()=>{for(let t=0;t<Math.max(a.length,o.length);t++){const e=a[t],i=o[t],n=t===o.length-1,h=t===a.length-1;if(i){if("wildcard"===i.type)return!!e?.value&&(r["*"]=f(a.slice(t).map((t=>t.value))),!0);if("pathname"===i.type){if("/"===i.value&&!e?.value)return!0;if(e)if(s.caseSensitive){if(i.value!==e.value)return!1}else if(i.value.toLowerCase()!==e.value.toLowerCase())return!1}if(!e)return!1;if("param"===i.type){if("/"===e?.value)return!1;"$"!==e.value.charAt(0)&&(r[i.value.substring(1)]=e.value)}}if(n&&!h)return!!s.fuzzy}return!0})()?r:void 0}function _(t,e){var s,a,o,r="";for(s in t)if(void 0!==(o=t[s]))if(Array.isArray(o))for(a=0;a<o.length;a++)r&&(r+="&"),r+=encodeURIComponent(s)+"="+encodeURIComponent(o[a]);else r&&(r+="&"),r+=encodeURIComponent(s)+"="+encodeURIComponent(o);return(e||"")+r}function x(t){if(!t)return"";var e=decodeURIComponent(t);return"false"!==e&&("true"===e||("0"===e.charAt(0)?e:0*+e==0?+e:e))}function E(t){for(var e,s,a={},o=t.split("&");e=o.shift();)void 0!==a[s=(e=e.split("=")).shift()]?a[s]=[].concat(a[s],x(e.shift())):a[s]=x(e.shift());return a}const R="__root__";class M{constructor(t){this.options=t||{},this.isRoot=!t?.getParentRoute}init=t=>{this.originalIndex=t.originalIndex,this.router=t.router;const s=this.options,a=!s?.path&&!s?.id,o=this.options?.getParentRoute?.();a?this.path=R:e(o);let r=a?R:s.path;r&&"/"!==r&&(r=w(r));const i=s?.id||r;let n=a?R:f([o.id===R?"":o.id,i]);r===R&&(r="/"),n!==R&&(n=f(["/",n]));const h=n===R?"/":y(f([o.fullPath,r]));this.path=r,this.id=n,this.fullPath=h};addChildren=t=>(this.children=t,this)}class C extends M{constructor(t){super(t)}static withRouterContext=()=>t=>new C(t)} | ||
/** | ||
@@ -22,4 +22,3 @@ * store | ||
* @license MIT | ||
*/ | ||
class ${listeners=new Set;batching=!1;queue=[];constructor(t,e){this.state=t,this.options=e}subscribe=t=>{this.listeners.add(t);const e=this.options?.onSubscribe?.(t,this);return()=>{this.listeners.delete(t),e?.()}};setState=t=>{const e=this.state;this.state=this.options?.updateFn?this.options.updateFn(e)(t):t(e),this.state!==e&&(this.queue.push((()=>{this.listeners.forEach((t=>t(this.state,e))),this.options?.onUpdate?.(this.state,e)})),this.#t())};#t=()=>{this.batching||(this.queue.forEach((t=>t())),this.queue=[])};batch=t=>{this.batching=!0,t(),this.batching=!1,this.#t()}}const A=["component","errorComponent","pendingComponent"];class C{abortController=new AbortController;onLoaderDataListeners=new Set;constructor(t,e,s){Object.assign(this,{route:e,router:t,id:s.id,pathname:s.pathname,params:s.params,store:new $({updatedAt:0,routeSearch:{},search:{},status:"idle"})}),this.#e()||this.store.setState((t=>({...t,status:"success"})))}cancel=()=>{this.abortController?.abort()};load=async t=>{"pending"!==this.store.state.status&&await this.fetch(t)};#s="";fetch=async t=>(this.__loadPromise=Promise.resolve().then((async()=>{const e=""+Date.now()+Math.random();this.#s=e;const s=()=>e!==this.#s?this.__loadPromise:void 0;let a;this.store.batch((()=>{"idle"===this.store.state.status&&this.store.setState((t=>({...t,status:"pending"})))}));const o=(async()=>{await Promise.all(A.map((async t=>{const e=this.route.options[t];this[t]?.preload&&(this[t]=await this.router.options.loadComponent(e))})))})(),r=Promise.resolve().then((()=>{if(this.route.options.onLoad)return this.route.options.onLoad({params:this.params,search:this.store.state.search,signal:this.abortController.signal,preload:!!t?.preload})}));try{if(await o,await r,a=s())return await a;this.store.setState((t=>({...t,error:void 0,status:"success",updatedAt:Date.now()})))}catch(t){this.store.setState((e=>({...e,error:t,status:"error",updatedAt:Date.now()})))}finally{delete this.__loadPromise}})),this.__loadPromise);#e=()=>!(!this.route.options.onLoad&&!A.some((t=>this.route.options[t]?.preload)));__setParentMatch=t=>{!this.parentMatch&&t&&(this.parentMatch=t)};__validate=()=>{const t=this.parentMatch?.store.state.search??this.router.store.state.latestLocation.search;try{let e=("object"==typeof this.route.options.validateSearch?this.route.options.validateSearch.parse:this.route.options.validateSearch)?.(t)??{};this.store.setState((s=>({...s,routeSearch:e,search:{...t,...e}}))),A.map((async t=>{const e=this.route.options[t];"function"!=typeof this[t]&&(this[t]=e)}))}catch(t){console.error(t);const e=new Error("Invalid search params found",{cause:t});return e.code="INVALID_SEARCH_PARAMS",void this.store.setState((t=>({...t,status:"error",error:e})))}}}const F=k(JSON.parse),I=T(JSON.stringify);function k(t){return e=>{"?"===e.substring(0,1)&&(e=e.substring(1));let s=R(e);for(let e in s){const a=s[e];if("string"==typeof a)try{s[e]=t(a)}catch(t){}}return s}}function T(t){return e=>{(e={...e})&&Object.keys(e).forEach((s=>{const a=e[s];if(void 0===a||void 0===a)delete e[s];else if(a&&"object"==typeof a&&null!==a)try{e[s]=t(a)}catch(t){}}));const s=P(e).toString();return s?`?${s}`:""}}const j=async({router:t,routeMatch:e})=>{const s=t.buildNext({to:".",search:t=>({...t??{},__data:{matchId:e.id}})}),a=await fetch(s.href,{method:"GET",signal:e.abortController.signal});if(a.ok)return a.json();throw new Error("Failed to fetch match data")};const D="undefined"==typeof window||!window.document.createElement;function O(){return{status:"idle",latestLocation:null,currentLocation:null,currentMatches:[],lastUpdated:Date.now()}}t.RootRoute=class extends x{constructor(t){super(t)}},t.Route=x,t.RouteMatch=C,t.Router=class{#a;startedLoadingAt=Date.now();resolveNavigation=()=>{};constructor(t){this.options={defaultPreloadDelay:50,context:void 0,...t,stringifySearch:t?.stringifySearch??I,parseSearch:t?.parseSearch??F,fetchServerDataFn:t?.fetchServerDataFn??j},this.store=new $(O()),this.basepath="",this.update(t),this.options.Router?.(this)}reset=()=>{this.store.setState((t=>Object.assign(t,O())))};mount=()=>{if(!D){this.store.state.currentMatches.length||this.load();const t="visibilitychange",e="focus";return window.addEventListener&&(window.addEventListener(t,this.#o,!1),window.addEventListener(e,this.#o,!1)),()=>{window.removeEventListener&&(window.removeEventListener(t,this.#o),window.removeEventListener(e,this.#o))}}return()=>{}};update=t=>{if(Object.assign(this.options,t),!this.history||this.options.history&&this.options.history!==this.history){this.#a&&this.#a(),this.history=this.options.history??(D?r():o());const t=this.#r();this.store.setState((e=>({...e,latestLocation:t,currentLocation:t}))),this.#a=this.history.listen((()=>{this.load(this.#r(this.store.state.latestLocation))}))}const{basepath:e,routeTree:s}=this.options;return this.basepath=`/${w(e??"")??""}`,s&&(this.routesById={},this.routeTree=this.#i(s)),this};buildNext=t=>{const e=this.#n(t),s=this.matchRoutes(e.pathname),a=s.map((t=>t.route.options.preSearchFilters??[])).flat().filter(Boolean),o=s.map((t=>t.route.options.postSearchFilters??[])).flat().filter(Boolean);return this.#n({...t,__preSearchFilters:a,__postSearchFilters:o})};cancelMatches=()=>{[...this.store.state.currentMatches,...this.store.state.pendingMatches||[]].forEach((t=>{t.cancel()}))};load=async t=>{let s=Date.now();const a=s;let o;this.startedLoadingAt=a,this.cancelMatches(),this.store.batch((()=>{t&&this.store.setState((e=>({...e,latestLocation:t}))),o=this.matchRoutes(this.store.state.latestLocation.pathname,{strictParseParams:!0}),this.store.setState((t=>({...t,status:"pending",pendingMatches:o,pendingLocation:this.store.state.latestLocation})))}));try{await this.loadMatches(o)}catch(t){console.warn(t),e(!1)}if(this.startedLoadingAt!==a)return this.navigationPromise;const r=this.store.state.currentMatches,i=[],n=[];r.forEach((t=>{o.find((e=>e.id===t.id))?n.push(t):i.push(t)}));const h=o.filter((t=>!r.find((e=>e.id===t.id))));s=Date.now(),i.forEach((t=>{t.__onExit?.({params:t.params,search:t.store.state.routeSearch}),"error"===t.store.state.status&&this.store.setState((t=>({...t,status:"idle",error:void 0})))})),n.forEach((t=>{t.route.options.onTransition?.({params:t.params,search:t.store.state.routeSearch})})),h.forEach((t=>{t.__onExit=t.route.options.onLoaded?.({params:t.params,search:t.store.state.search})})),this.store.setState((t=>({...t,status:"idle",currentLocation:this.store.state.latestLocation,currentMatches:o,pendingLocation:void 0,pendingMatches:void 0}))),this.options.onRouteChange?.(),this.resolveNavigation()};getRoute=t=>{const s=this.routesById[t];return e(s),s};loadRoute=async(t=this.store.state.latestLocation)=>{const e=this.buildNext(t),s=this.matchRoutes(e.pathname,{strictParseParams:!0});return await this.loadMatches(s),s};preloadRoute=async(t=this.store.state.latestLocation)=>{const e=this.buildNext(t),s=this.matchRoutes(e.pathname,{strictParseParams:!0});return await this.loadMatches(s,{preload:!0}),s};matchRoutes=(t,e)=>{const s=[];if(!this.routeTree)return s;const a=[...this.store.state.currentMatches,...this.store.state.pendingMatches??[]],o=async r=>{let i=h(s)?.params??{};const n=this.options.filterRoutes?.(r)??r;let c=[];const u=(s,a)=>(a.some((a=>{const o=a.children;if(!a.path&&o?.length)return u([...c,a],o);const r=!("/"===a.path&&!o?.length),n=S(this.basepath,t,{to:a.fullPath,fuzzy:r,caseSensitive:a.options.caseSensitive??this.options.caseSensitive});if(n){let t;try{t=a.options.parseParams?.(n)??n}catch(t){if(e?.strictParseParams)throw t}i={...i,...t}}return n&&(c=[...s,a]),!!c.length})),!!c.length);if(u([],n),!c.length)return;c.forEach((t=>{const e=L(t.path,i),o=L(t.id,i,!0),r=a.find((t=>t.id===o))||new C(this,t,{id:o,params:i,pathname:f([this.basepath,e])});s.push(r)}));const l=h(c).children;l?.length&&o(l)};return o([this.routeTree]),s};loadMatches=async(t,e)=>{var s;(s=t).forEach(((t,e)=>{const a=s[e-1];a&&t.__setParentMatch(a)})),t.forEach((async t=>{t.__validate()})),await Promise.all(t.map((async t=>{try{await(t.route.options.beforeLoad?.({router:this,match:t}))}catch(s){throw e?.preload||t.route.options.onLoadError?.(s),s}})));const a=t.map((async(s,a)=>{const o=t[1],r=s.store.state.search;r.__data?.matchId&&r.__data.matchId!==s.id||(s.load({preload:e?.preload}),"success"!==s.store.state.status&&s.__loadPromise&&await s.__loadPromise,o&&await o.__loadPromise)}));await Promise.all(a)};reload=()=>{this.navigate({fromCurrent:!0,replace:!0,search:!0})};resolvePath=(t,e)=>v(this.basepath,t,m(e));navigate=async({from:t,to:s=".",search:a,hash:o,replace:r,params:i})=>{const n=String(s),h=void 0===t?t:String(t);let c;try{new URL(`${n}`),c=!0}catch(t){}return e(!c),this.#h({from:h,to:n,search:a,hash:o,replace:r,params:i})};matchRoute=(t,e)=>{t={...t,to:t.to?this.resolvePath(t.from??"",t.to):void 0};const s=this.buildNext(t);return e?.pending?!!this.store.state.pendingLocation&&S(this.basepath,this.store.state.pendingLocation.pathname,{...e,to:s.pathname}):S(this.basepath,this.store.state.currentLocation.pathname,{...e,to:s.pathname})};buildLink=({from:t,to:e=".",search:s,params:a,hash:o,target:r,replace:i,activeOptions:n,preload:h,preloadMaxAge:c,preloadGcMaxAge:u,preloadDelay:l,disabled:p})=>{try{return new URL(`${e}`),{type:"external",href:e}}catch(t){}const d={from:t,to:e,search:s,params:a,hash:o,replace:i},f=this.buildNext(d);h=h??this.options.defaultPreload;const m=l??this.options.defaultPreloadDelay??0,g=this.store.state.currentLocation.pathname===f.pathname,y=this.store.state.currentLocation.pathname.split("/"),w=f.pathname.split("/").every(((t,e)=>t===y[e])),v=this.store.state.currentLocation.hash===f.hash;return{type:"internal",next:f,handleFocus:t=>{h&&this.preloadRoute(d).catch((t=>{console.warn(t),console.warn("Error preloading route! ☝️")}))},handleClick:t=>{p||function(t){return!!(t.metaKey||t.altKey||t.ctrlKey||t.shiftKey)}(t)||t.defaultPrevented||r&&"_self"!==r||0!==t.button||(t.preventDefault(),this.#h(d))},handleEnter:t=>{const e=t.target||{};if(h){if(e.preloadTimeout)return;e.preloadTimeout=setTimeout((()=>{e.preloadTimeout=null,this.preloadRoute(d).catch((t=>{console.warn(t),console.warn("Error preloading route! ☝️")}))}),m)}},handleLeave:t=>{const e=t.target||{};e.preloadTimeout&&(clearTimeout(e.preloadTimeout),e.preloadTimeout=null)},isActive:(n?.exact?g:w)&&(!n?.includeHash||v),disabled:p}};dehydrate=()=>({state:{...u(this.store.state,["latestLocation","currentLocation","status","lastUpdated"]),currentMatches:this.store.state.currentMatches.map((t=>({id:t.id,state:{...u(t.store.state,["status"])}})))},context:this.options.context});hydrate=t=>{this.store.setState((s=>{this.options.context=t.context;const a=this.matchRoutes(t.state.latestLocation.pathname,{strictParseParams:!0});return a.forEach(((s,a)=>{const o=t.state.currentMatches[a];e(o&&o.id===s.id),s.store.setState((t=>({...t,...o.state})))})),a.forEach((t=>t.__validate())),{...s,...t.state,currentMatches:a}}))};#i=t=>{const e=t=>{t.forEach(((t,s)=>{t.init(),t.originalIndex=s,t.router=this;if(this.routesById[t.id])throw new Error;this.routesById[t.id]=t;const a=t.children;a?.length&&e(a)}))};return e([t]),t};#r=t=>{let{pathname:e,search:s,hash:a,state:o}=this.history.location;const r=this.options.parseSearch(s);return{pathname:e,searchStr:s,search:l(t?.search,r),hash:a.split("#").reverse()[0]??"",href:`${e}${s}${a}`,state:o,key:o?.key||"__init__"}};#o=()=>{this.load()};#n=(t={})=>{const e=t.fromCurrent?this.store.state.latestLocation.pathname:t.from??this.store.state.latestLocation.pathname;let s=v(this.basepath??"/",e,`${t.to??"."}`);const a=this.matchRoutes(this.store.state.latestLocation.pathname,{strictParseParams:!0}),o=this.matchRoutes(s),r={...h(a)?.params};let i=!0===(t.params??!0)?r:c(t.params,r);i&&o.map((t=>t.route.options.stringifyParams)).filter(Boolean).forEach((t=>{Object.assign({},i,t(i))})),s=L(s,i??{});const n=t.__preSearchFilters?.length?t.__preSearchFilters?.reduce(((t,e)=>e(t)),this.store.state.latestLocation.search):this.store.state.latestLocation.search,u=!0===t.search?n:t.search?c(t.search,n)??{}:t.__preSearchFilters?.length?n:{},p=t.__postSearchFilters?.length?t.__postSearchFilters.reduce(((t,e)=>e(t)),u):u,d=l(this.store.state.latestLocation.search,p),f=this.options.stringifySearch(d);let m=!0===t.hash?this.store.state.latestLocation.hash:c(t.hash,this.store.state.latestLocation.hash);return m=m?`#${m}`:"",{pathname:s,search:d,searchStr:f,state:this.store.state.latestLocation.state,hash:m,href:`${s}${f}${m}`,key:t.key}};#h=t=>{const e=this.buildNext(t),s=""+Date.now()+Math.random();this.navigateTimeout&&clearTimeout(this.navigateTimeout);let a="replace";t.replace||(a="push");this.store.state.latestLocation.href===e.href&&!e.key&&(a="replace");const o=`${e.pathname}${e.searchStr}${e.hash?`#${e.hash}`:""}`;return this.history["push"===a?"push":"replace"](o,{id:s,...e.state}),this.navigationPromise=new Promise((t=>{const e=this.resolveNavigation;this.resolveNavigation=()=>{e(),t()}}))}},t.cleanPath=m,t.createBrowserHistory=o,t.createHashHistory=function(){return o({getHref:()=>window.location.hash.substring(1),createHref:t=>`#${t}`})},t.createMemoryHistory=r,t.decode=R,t.defaultFetchServerDataFn=j,t.defaultParseSearch=F,t.defaultStringifySearch=I,t.encode=P,t.functionalUpdate=c,t.interpolatePath=L,t.invariant=e,t.isPlainObject=p,t.joinPaths=f,t.last=h,t.matchByPath=_,t.matchPathname=S,t.parsePathname=b,t.parseSearchWith=k,t.pick=u,t.replaceEqualDeep=l,t.resolvePath=v,t.rootRouteId=M,t.stringifySearchWith=T,t.trimPath=w,t.trimPathLeft=g,t.trimPathRight=y,t.warning=function(t,e){if(t){"undefined"!=typeof console&&console.warn(e);try{throw new Error(e)}catch{}}return!0},Object.defineProperty(t,"__esModule",{value:!0})})); | ||
*/class ${listeners=new Set;batching=!1;queue=[];constructor(t,e){this.state=t,this.options=e}subscribe=t=>{this.listeners.add(t);const e=this.options?.onSubscribe?.(t,this);return()=>{this.listeners.delete(t),e?.()}};setState=t=>{const e=this.state;this.state=this.options?.updateFn?this.options.updateFn(e)(t):t(e),this.state!==e&&(this.options?.onUpdate?.(this.state,e),this.queue.push((()=>{this.listeners.forEach((t=>t(this.state,e)))})),this.#t())};#t=()=>{this.batching||(this.queue.forEach((t=>t())),this.queue=[])};batch=t=>{this.batching=!0,t(),this.batching=!1,this.#t()}}const A=["component","errorComponent","pendingComponent"];class F{abortController=new AbortController;onLoaderDataListeners=new Set;constructor(t,e,s){Object.assign(this,{route:e,router:t,id:s.id,pathname:s.pathname,params:s.params,store:new $({updatedAt:0,routeSearch:{},search:{},status:"idle"},{onUpdate:t=>{this.state=t}})}),this.state=this.store.state,this.#e()||this.store.setState((t=>({...t,status:"success"})))}#e=()=>!(!this.route.options.onLoad&&!A.some((t=>this.route.options[t]?.preload)));__init=t=>{this.parentMatch=t.parentMatch;const e=this.parentMatch?.state.search??this.router.state.latestLocation.search;try{let t=("object"==typeof this.route.options.validateSearch?this.route.options.validateSearch.parse:this.route.options.validateSearch)?.(e)??{};this.store.setState((s=>({...s,routeSearch:t,search:{...e,...t}}))),A.map((async t=>{const e=this.route.options[t];"function"!=typeof this[t]&&(this[t]=e)}));const s=this.parentMatch;this.routeContext=this.route.options.getContext?.({parentContext:s?.routeContext,context:s?.context,params:this.params,search:this.state.search})||{},this.context=s?{...s.context,...this.routeContext}:{...this.router?.options.context,...this.routeContext}}catch(t){console.error(t);const e=new Error("Invalid search params found",{cause:t});return e.code="INVALID_SEARCH_PARAMS",void this.store.setState((t=>({...t,status:"error",error:e})))}};cancel=()=>{this.abortController?.abort()};load=async t=>{"pending"!==this.state.status&&await this.fetch(t)};#s="";fetch=async t=>(this.__loadPromise=Promise.resolve().then((async()=>{const e=""+Date.now()+Math.random();this.#s=e;const s=()=>e!==this.#s?this.__loadPromise:void 0;let a;this.store.batch((()=>{"idle"===this.state.status&&this.store.setState((t=>({...t,status:"pending"})))}));const o=(async()=>{await Promise.all(A.map((async t=>{const e=this.route.options[t];this[t]?.preload&&(this[t]=await this.router.options.loadComponent(e))})))})(),r=Promise.resolve().then((()=>{if(this.route.options.onLoad)return this.route.options.onLoad({params:this.params,search:this.state.search,signal:this.abortController.signal,preload:!!t?.preload,routeContext:this.routeContext,context:this.context})}));try{if(await o,await r,a=s())return await a;this.store.setState((t=>({...t,error:void 0,status:"success",updatedAt:Date.now()})))}catch(t){this.store.setState((e=>({...e,error:t,status:"error",updatedAt:Date.now()})))}finally{delete this.__loadPromise}})),this.__loadPromise)}const I=T(JSON.parse),k=j(JSON.stringify);function T(t){return e=>{"?"===e.substring(0,1)&&(e=e.substring(1));let s=E(e);for(let e in s){const a=s[e];if("string"==typeof a)try{s[e]=t(a)}catch(t){}}return s}}function j(t){return e=>{(e={...e})&&Object.keys(e).forEach((s=>{const a=e[s];if(void 0===a||void 0===a)delete e[s];else if(a&&"object"==typeof a&&null!==a)try{e[s]=t(a)}catch(t){}}));const s=_(e).toString();return s?`?${s}`:""}}const D=async({router:t,routeMatch:e})=>{const s=t.buildNext({to:".",search:t=>({...t??{},__data:{matchId:e.id}})}),a=await fetch(s.href,{method:"GET",signal:e.abortController.signal});if(a.ok)return a.json();throw new Error("Failed to fetch match data")};const O="undefined"==typeof window||!window.document.createElement;function H(){return{status:"idle",latestLocation:null,currentLocation:null,currentMatches:[],lastUpdated:Date.now()}}function N(t){t.forEach(((e,s)=>{const a=t[s-1];e.__init({parentMatch:a})}))}t.RootRoute=C,t.Route=M,t.RouteMatch=F,t.Router=class{#a;startedLoadingAt=Date.now();resolveNavigation=()=>{};constructor(t){this.options={defaultPreloadDelay:50,context:void 0,...t,stringifySearch:t?.stringifySearch??k,parseSearch:t?.parseSearch??I,fetchServerDataFn:t?.fetchServerDataFn??D},this.store=new $(H(),{onUpdate:t=>{this.state=t}}),this.state=this.store.state,this.basepath="",this.update(t),this.options.Router?.(this)}reset=()=>{this.store.setState((t=>Object.assign(t,H())))};mount=()=>{if(!O){this.state.currentMatches.length||this.load();const t="visibilitychange",e="focus";return window.addEventListener&&(window.addEventListener(t,this.#o,!1),window.addEventListener(e,this.#o,!1)),()=>{window.removeEventListener&&(window.removeEventListener(t,this.#o),window.removeEventListener(e,this.#o))}}return()=>{}};update=t=>{if(Object.assign(this.options,t),!this.history||this.options.history&&this.options.history!==this.history){this.#a&&this.#a(),this.history=this.options.history??(O?r():o());const t=this.#r();this.store.setState((e=>({...e,latestLocation:t,currentLocation:t}))),this.#a=this.history.listen((()=>{this.load({next:this.#r(this.state.latestLocation)})}))}const{basepath:e,routeTree:s}=this.options;return this.basepath=`/${w(e??"")??""}`,s&&(this.routesById={},this.routeTree=this.#i(s)),this};buildNext=t=>{const e=this.#n(t),s=this.matchRoutes(e.pathname),a=s.map((t=>t.route.options.preSearchFilters??[])).flat().filter(Boolean),o=s.map((t=>t.route.options.postSearchFilters??[])).flat().filter(Boolean);return this.#n({...t,__preSearchFilters:a,__postSearchFilters:o})};cancelMatches=()=>{[...this.state.currentMatches,...this.state.pendingMatches||[]].forEach((t=>{t.cancel()}))};load=async t=>{let s=Date.now();const a=s;let o;this.startedLoadingAt=a,this.cancelMatches(),this.store.batch((()=>{t?.next&&this.store.setState((e=>({...e,latestLocation:t.next}))),o=this.matchRoutes(this.state.latestLocation.pathname,{strictParseParams:!0}),this.store.setState((t=>({...t,status:"pending",pendingMatches:o,pendingLocation:this.state.latestLocation})))}));try{await this.loadMatches(o)}catch(t){console.warn(t),e(!1)}if(this.startedLoadingAt!==a)return this.navigationPromise;const r=this.state.currentMatches,i=[],n=[];r.forEach((t=>{o.find((e=>e.id===t.id))?n.push(t):i.push(t)}));const h=o.filter((t=>!r.find((e=>e.id===t.id))));s=Date.now(),i.forEach((t=>{t.__onExit?.({params:t.params,search:t.state.routeSearch}),"error"===t.state.status&&this.store.setState((t=>({...t,status:"idle",error:void 0})))})),n.forEach((t=>{t.route.options.onTransition?.({params:t.params,search:t.state.routeSearch})})),h.forEach((t=>{t.__onExit=t.route.options.onLoaded?.({params:t.params,search:t.state.search})})),this.store.setState((t=>({...t,status:"idle",currentLocation:this.state.latestLocation,currentMatches:o,pendingLocation:void 0,pendingMatches:void 0}))),this.options.onRouteChange?.(),this.resolveNavigation()};getRoute=t=>{const s=this.routesById[t];return e(s),s};loadRoute=async(t=this.state.latestLocation)=>{const e=this.buildNext(t),s=this.matchRoutes(e.pathname,{strictParseParams:!0});return await this.loadMatches(s),s};preloadRoute=async(t=this.state.latestLocation)=>{const e=this.buildNext(t),s=this.matchRoutes(e.pathname,{strictParseParams:!0});return await this.loadMatches(s,{preload:!0}),s};matchRoutes=(t,e)=>{const s=[];if(!this.routeTree)return s;const a=[...this.state.currentMatches,...this.state.pendingMatches??[]],o=async r=>{let i=h(s)?.params??{};const n=this.options.filterRoutes?.(r)??r;let c=[];const u=(s,a)=>(a.some((a=>{const o=a.children;if(!a.path&&o?.length)return u([...c,a],o);const r=!("/"===a.path&&!o?.length),n=S(this.basepath,t,{to:a.fullPath,fuzzy:r,caseSensitive:a.options.caseSensitive??this.options.caseSensitive});if(n){let t;try{t=a.options.parseParams?.(n)??n}catch(t){if(e?.strictParseParams)throw t}i={...i,...t}}return n&&(c=[...s,a]),!!c.length})),!!c.length);if(u([],n),!c.length)return;c.forEach((t=>{const e=L(t.path,i),o=L(t.id,i,!0),r=a.find((t=>t.id===o))||new F(this,t,{id:o,params:i,pathname:f([this.basepath,e])});s.push(r)}));const l=h(c).children;l?.length&&o(l)};return o([this.routeTree]),s};loadMatches=async(t,e)=>{N(t),await Promise.all(t.map((async t=>{try{await(t.route.options.beforeLoad?.({router:this,match:t}))}catch(s){throw e?.preload||t.route.options.onLoadError?.(s),s}})));const s=t.map((async(s,a)=>{const o=t[1];s.state.search,s.load({preload:e?.preload}),"success"!==s.state.status&&s.__loadPromise&&await s.__loadPromise,o&&await o.__loadPromise}));await Promise.all(s)};reload=()=>{this.navigate({fromCurrent:!0,replace:!0,search:!0})};resolvePath=(t,e)=>v(this.basepath,t,m(e));navigate=async({from:t,to:s=".",search:a,hash:o,replace:r,params:i})=>{const n=String(s),h=void 0===t?t:String(t);let c;try{new URL(`${n}`),c=!0}catch(t){}return e(!c),this.#h({from:h,to:n,search:a,hash:o,replace:r,params:i})};matchRoute=(t,e)=>{t={...t,to:t.to?this.resolvePath(t.from??"",t.to):void 0};const s=this.buildNext(t);return e?.pending?!!this.state.pendingLocation&&S(this.basepath,this.state.pendingLocation.pathname,{...e,to:s.pathname}):S(this.basepath,this.state.currentLocation.pathname,{...e,to:s.pathname})};buildLink=({from:t,to:e=".",search:s,params:a,hash:o,target:r,replace:i,activeOptions:n,preload:h,preloadMaxAge:c,preloadGcMaxAge:u,preloadDelay:l,disabled:p})=>{try{return new URL(`${e}`),{type:"external",href:e}}catch(t){}const d={from:t,to:e,search:s,params:a,hash:o,replace:i},f=this.buildNext(d);h=h??this.options.defaultPreload;const m=l??this.options.defaultPreloadDelay??0,g=this.state.currentLocation.pathname===f.pathname,y=this.state.currentLocation.pathname.split("/"),w=f.pathname.split("/").every(((t,e)=>t===y[e])),v=this.state.currentLocation.hash===f.hash;return{type:"internal",next:f,handleFocus:t=>{h&&this.preloadRoute(d).catch((t=>{console.warn(t),console.warn("Error preloading route! ☝️")}))},handleClick:t=>{p||function(t){return!!(t.metaKey||t.altKey||t.ctrlKey||t.shiftKey)}(t)||t.defaultPrevented||r&&"_self"!==r||0!==t.button||(t.preventDefault(),this.#h(d))},handleEnter:t=>{const e=t.target||{};if(h){if(e.preloadTimeout)return;e.preloadTimeout=setTimeout((()=>{e.preloadTimeout=null,this.preloadRoute(d).catch((t=>{console.warn(t),console.warn("Error preloading route! ☝️")}))}),m)}},handleLeave:t=>{const e=t.target||{};e.preloadTimeout&&(clearTimeout(e.preloadTimeout),e.preloadTimeout=null)},isActive:(n?.exact?g:w)&&(!n?.includeHash||v),disabled:p}};dehydrate=()=>({state:{...u(this.state,["latestLocation","currentLocation","status","lastUpdated"]),currentMatches:this.state.currentMatches.map((t=>({id:t.id,state:{status:t.state.status}})))}});hydrate=t=>{this.store.setState((s=>{const a=this.matchRoutes(t.state.latestLocation.pathname,{strictParseParams:!0});return a.forEach(((s,a)=>{const o=t.state.currentMatches[a];e(o&&o.id===s.id),s.store.setState((t=>({...t,...o.state})))})),N(a),{...s,...t.state,currentMatches:a}}))};#i=t=>{const e=t=>{t.forEach(((t,s)=>{t.init({originalIndex:s,router:this});if(this.routesById[t.id])throw new Error;this.routesById[t.id]=t;const a=t.children;a?.length&&e(a)}))};return e([t]),t};#r=t=>{let{pathname:e,search:s,hash:a,state:o}=this.history.location;const r=this.options.parseSearch(s);return{pathname:e,searchStr:s,search:l(t?.search,r),hash:a.split("#").reverse()[0]??"",href:`${e}${s}${a}`,state:o,key:o?.key||"__init__"}};#o=()=>{this.load()};#n=(t={})=>{const e=t.fromCurrent?this.state.latestLocation.pathname:t.from??this.state.latestLocation.pathname;let s=v(this.basepath??"/",e,`${t.to??"."}`);const a=this.matchRoutes(this.state.latestLocation.pathname,{strictParseParams:!0}),o=this.matchRoutes(s),r={...h(a)?.params};let i=!0===(t.params??!0)?r:c(t.params,r);i&&o.map((t=>t.route.options.stringifyParams)).filter(Boolean).forEach((t=>{Object.assign({},i,t(i))})),s=L(s,i??{});const n=t.__preSearchFilters?.length?t.__preSearchFilters?.reduce(((t,e)=>e(t)),this.state.latestLocation.search):this.state.latestLocation.search,u=!0===t.search?n:t.search?c(t.search,n)??{}:t.__preSearchFilters?.length?n:{},p=t.__postSearchFilters?.length?t.__postSearchFilters.reduce(((t,e)=>e(t)),u):u,d=l(this.state.latestLocation.search,p),f=this.options.stringifySearch(d);let m=!0===t.hash?this.state.latestLocation.hash:c(t.hash,this.state.latestLocation.hash);return m=m?`#${m}`:"",{pathname:s,search:d,searchStr:f,state:this.state.latestLocation.state,hash:m,href:`${s}${f}${m}`,key:t.key}};#h=t=>{const e=this.buildNext(t),s=""+Date.now()+Math.random();this.navigateTimeout&&clearTimeout(this.navigateTimeout);let a="replace";t.replace||(a="push");this.state.latestLocation.href===e.href&&!e.key&&(a="replace");const o=`${e.pathname}${e.searchStr}${e.hash?`#${e.hash}`:""}`;return this.history["push"===a?"push":"replace"](o,{id:s,...e.state}),this.navigationPromise=new Promise((t=>{const e=this.resolveNavigation;this.resolveNavigation=()=>{e(),t()}}))}},t.cleanPath=m,t.createBrowserHistory=o,t.createHashHistory=function(){return o({getHref:()=>window.location.hash.substring(1),createHref:t=>`#${t}`})},t.createMemoryHistory=r,t.decode=E,t.defaultFetchServerDataFn=D,t.defaultParseSearch=I,t.defaultStringifySearch=k,t.encode=_,t.functionalUpdate=c,t.interpolatePath=L,t.invariant=e,t.isPlainObject=p,t.joinPaths=f,t.last=h,t.matchByPath=P,t.matchPathname=S,t.parsePathname=b,t.parseSearchWith=T,t.pick=u,t.replaceEqualDeep=l,t.resolvePath=v,t.rootRouteId=R,t.stringifySearchWith=j,t.trimPath=w,t.trimPathLeft=g,t.trimPathRight=y,t.warning=function(t,e){if(t){"undefined"!=typeof console&&console.warn(e);try{throw new Error(e)}catch{}}return!0},Object.defineProperty(t,"__esModule",{value:!0})})); | ||
//# sourceMappingURL=index.production.js.map |
{ | ||
"name": "@tanstack/router", | ||
"author": "Tanner Linsley", | ||
"version": "0.0.1-beta.61", | ||
"version": "0.0.1-beta.62", | ||
"license": "MIT", | ||
@@ -40,4 +40,4 @@ "repository": "tanstack/router", | ||
"tiny-invariant": "^1.3.1", | ||
"@tanstack/store": "0.0.1-beta.57" | ||
"@tanstack/store": "0.0.1-beta.62" | ||
} | ||
} |
@@ -5,4 +5,2 @@ // While the public API was clearly inspired by the "history" npm package, | ||
import { match } from 'assert' | ||
export interface RouterHistory { | ||
@@ -9,0 +7,0 @@ location: RouterLocation |
@@ -149,9 +149,7 @@ import { AnyRoutesInfo, DefaultRoutesInfo, RouteByPath } from './routeInfo' | ||
TTo, | ||
TFromSchema = Expand< | ||
UnionToIntersection< | ||
TRoutesInfo['fullSearchSchema'] & | ||
RouteByPath<TRoutesInfo, TFrom> extends never | ||
? {} | ||
: RouteByPath<TRoutesInfo, TFrom>['__types']['fullSearchSchema'] | ||
> | ||
TFromSchema = UnionToIntersection< | ||
TRoutesInfo['fullSearchSchema'] & | ||
RouteByPath<TRoutesInfo, TFrom> extends never | ||
? {} | ||
: RouteByPath<TRoutesInfo, TFrom>['__types']['fullSearchSchema'] | ||
>, | ||
@@ -169,7 +167,7 @@ // Find the schema for the new path, and make optional any keys | ||
>, | ||
TFromFullSchema = Expand< | ||
UnionToIntersection<TRoutesInfo['fullSearchSchema'] & TFromSchema> | ||
TFromFullSchema = UnionToIntersection< | ||
TRoutesInfo['fullSearchSchema'] & TFromSchema | ||
>, | ||
TToFullSchema = Expand< | ||
UnionToIntersection<TRoutesInfo['fullSearchSchema'] & TToSchema> | ||
TToFullSchema = UnionToIntersection< | ||
TRoutesInfo['fullSearchSchema'] & TToSchema | ||
>, | ||
@@ -192,8 +190,6 @@ > = keyof PickRequired<TToSchema> extends never | ||
TTo, | ||
TFromSchema = Expand< | ||
UnionToIntersection< | ||
RouteByPath<TRoutesInfo, TFrom> extends never | ||
? {} | ||
: RouteByPath<TRoutesInfo, TFrom>['__types']['allParams'] | ||
> | ||
TFromSchema = UnionToIntersection< | ||
RouteByPath<TRoutesInfo, TFrom> extends never | ||
? {} | ||
: RouteByPath<TRoutesInfo, TFrom>['__types']['allParams'] | ||
>, | ||
@@ -209,8 +205,4 @@ // Find the schema for the new path, and make optional any keys | ||
>, | ||
TFromFullParams = Expand< | ||
UnionToIntersection<TRoutesInfo['allParams'] & TFromSchema> | ||
>, | ||
TToFullParams = Expand< | ||
UnionToIntersection<TRoutesInfo['allParams'] & TToSchema> | ||
>, | ||
TFromFullParams = UnionToIntersection<TRoutesInfo['allParams'] & TFromSchema>, | ||
TToFullParams = UnionToIntersection<TRoutesInfo['allParams'] & TToSchema>, | ||
> = keyof PickRequired<TToSchema> extends never | ||
@@ -302,6 +294,3 @@ ? { | ||
export type CheckPathError< | ||
TRoutesInfo extends AnyRoutesInfo, | ||
TInvalids, | ||
> = Expand<{ | ||
export type CheckPathError<TRoutesInfo extends AnyRoutesInfo, TInvalids> = { | ||
Error: `${TInvalids extends string | ||
@@ -311,3 +300,3 @@ ? TInvalids | ||
'Valid Route Paths': TRoutesInfo['routePaths'] | ||
}> | ||
} | ||
@@ -321,6 +310,3 @@ export type CheckId<TRoutesInfo extends AnyRoutesInfo, TPath, TPass> = Exclude< | ||
export type CheckIdError< | ||
TRoutesInfo extends AnyRoutesInfo, | ||
TInvalids, | ||
> = Expand<{ | ||
export type CheckIdError<TRoutesInfo extends AnyRoutesInfo, TInvalids> = { | ||
Error: `${TInvalids extends string | ||
@@ -330,3 +316,3 @@ ? TInvalids | ||
'Valid Route IDs': TRoutesInfo['routeIds'] | ||
}> | ||
} | ||
@@ -333,0 +319,0 @@ export type ResolveRelativePath<TFrom, TTo = '.'> = TFrom extends string |
301
src/route.ts
import { GetFrameworkGeneric } from './frameworks' | ||
import { ParsePathParams } from './link' | ||
import { RouteMatch } from './routeMatch' | ||
import { AnyRouter, Router } from './router' | ||
import { AnyRouter, RegisteredRouter, Router } from './router' | ||
import { | ||
@@ -22,2 +22,3 @@ Expand, | ||
export type AnySearchSchema = {} | ||
export type AnyContext = {} | ||
export interface RouteMeta {} | ||
@@ -36,2 +37,4 @@ | ||
export interface FrameworkRouteOptions {} | ||
export type RouteOptions< | ||
@@ -50,53 +53,70 @@ TParentRoute extends AnyRoute = AnyRoute, | ||
TAllParams extends AnyPathParams = {}, | ||
> = RouteOptionsBase<TCustomId, TPath> & { | ||
getParentRoute: () => TParentRoute | ||
// If true, this route will be matched as case-sensitive | ||
caseSensitive?: boolean | ||
validateSearch?: SearchSchemaValidator<TSearchSchema, TParentSearchSchema> | ||
// Filter functions that can manipulate search params *before* they are passed to links and navigate | ||
// calls that match this route. | ||
preSearchFilters?: SearchFilter<TFullSearchSchema>[] | ||
// Filter functions that can manipulate search params *after* they are passed to links and navigate | ||
// calls that match this route. | ||
postSearchFilters?: SearchFilter<TFullSearchSchema>[] | ||
// The content to be rendered when the route is matched. If no component is provided, defaults to `<Outlet />` | ||
component?: GetFrameworkGeneric<'Component'> // , NoInfer<TParentAllLoaderData>> | ||
// The content to be rendered when the route encounters an error | ||
errorComponent?: GetFrameworkGeneric<'ErrorComponent'> // , NoInfer<TParentAllLoaderData>> | ||
// If supported by your framework, the content to be rendered as the fallback content until the route is ready to render | ||
pendingComponent?: GetFrameworkGeneric<'Component'> //, NoInfer<TParentAllLoaderData>> | ||
TParentContext extends AnyContext = AnyContext, | ||
TAllParentContext extends AnyContext = AnyContext, | ||
TRouteContext extends AnyContext = AnyContext, | ||
TContext extends AnyContext = TRouteContext, | ||
> = RouteOptionsBase<TCustomId, TPath> & | ||
FrameworkRouteOptions & { | ||
getParentRoute: () => TParentRoute | ||
// If true, this route will be matched as case-sensitive | ||
caseSensitive?: boolean | ||
validateSearch?: SearchSchemaValidator<TSearchSchema, TParentSearchSchema> | ||
// Filter functions that can manipulate search params *before* they are passed to links and navigate | ||
// calls that match this route. | ||
preSearchFilters?: SearchFilter<TFullSearchSchema>[] | ||
// Filter functions that can manipulate search params *after* they are passed to links and navigate | ||
// calls that match this route. | ||
postSearchFilters?: SearchFilter<TFullSearchSchema>[] | ||
// The content to be rendered when the route is matched. If no component is provided, defaults to `<Outlet />` | ||
component?: GetFrameworkGeneric<'Component'> // , NoInfer<TParentAllLoaderData>> | ||
// The content to be rendered when the route encounters an error | ||
errorComponent?: GetFrameworkGeneric<'ErrorComponent'> // , NoInfer<TParentAllLoaderData>> | ||
// If supported by your framework, the content to be rendered as the fallback content until the route is ready to render | ||
pendingComponent?: GetFrameworkGeneric<'Component'> //, NoInfer<TParentAllLoaderData>> | ||
// This async function is called before a route is loaded. | ||
// If an error is thrown here, the route's loader will not be called. | ||
// If thrown during a navigation, the navigation will be cancelled and the error will be passed to the `onLoadError` function. | ||
// If thrown during a preload event, the error will be logged to the console. | ||
beforeLoad?: (opts: { | ||
router: AnyRouter | ||
match: RouteMatch | ||
}) => Promise<void> | void | ||
// This async function is called before a route is loaded. | ||
// If an error is thrown here, the route's loader will not be called. | ||
// If thrown during a navigation, the navigation will be cancelled and the error will be passed to the `onLoadError` function. | ||
// If thrown during a preload event, the error will be logged to the console. | ||
beforeLoad?: (opts: { | ||
router: AnyRouter | ||
match: RouteMatch | ||
}) => Promise<void> | void | ||
// An asynchronous function responsible for preparing or fetching data for the route before it is rendered | ||
onLoad?: OnLoadFn<TFullSearchSchema, TAllParams, TRouteContext, TContext> | ||
// An asynchronous function responsible for preparing or fetching data for the route before it is rendered | ||
onLoad?: OnLoadFn<TFullSearchSchema, TAllParams> | ||
// This function will be called if the route's loader throws an error **during an attempted navigation**. | ||
// If you want to redirect due to an error, call `router.navigate()` from within this function. | ||
onLoadError?: (err: any) => void | ||
// This function is called | ||
// when moving from an inactive state to an active one. Likewise, when moving from | ||
// an active to an inactive state, the return function (if provided) is called. | ||
onLoaded?: (matchContext: { | ||
params: TAllParams | ||
search: TFullSearchSchema | ||
}) => | ||
| void | ||
| undefined | ||
| ((match: { params: TAllParams; search: TFullSearchSchema }) => void) | ||
// This function is called when the route remains active from one transition to the next. | ||
onTransition?: (match: { | ||
params: TAllParams | ||
search: TFullSearchSchema | ||
}) => void | ||
// An object of whatever you want! This object is accessible anywhere matches are. | ||
meta?: RouteMeta // TODO: Make this nested and mergeable | ||
} & ( | ||
// This function will be called if the route's loader throws an error **during an attempted navigation**. | ||
// If you want to redirect due to an error, call `router.navigate()` from within this function. | ||
onLoadError?: (err: any) => void | ||
// This function is called | ||
// when moving from an inactive state to an active one. Likewise, when moving from | ||
// an active to an inactive state, the return function (if provided) is called. | ||
onLoaded?: (matchContext: { | ||
params: TAllParams | ||
search: TFullSearchSchema | ||
}) => | ||
| void | ||
| undefined | ||
| ((match: { params: TAllParams; search: TFullSearchSchema }) => void) | ||
// This function is called when the route remains active from one transition to the next. | ||
onTransition?: (match: { | ||
params: TAllParams | ||
search: TFullSearchSchema | ||
}) => void | ||
// An object of whatever you want! This object is accessible anywhere matches are. | ||
getContext?: ( | ||
opts: { | ||
params: TAllParams | ||
search: TFullSearchSchema | ||
} & (TParentRoute extends undefined | ||
? { | ||
context?: TAllParentContext | ||
parentContext?: TParentContext | ||
} | ||
: { | ||
context: TAllParentContext | ||
parentContext: TParentContext | ||
}), | ||
) => TRouteContext | ||
} & ( | ||
| { | ||
@@ -152,4 +172,11 @@ parseParams?: never | ||
TAllParams extends AnyPathParams = {}, | ||
TContext extends AnyContext = AnyContext, | ||
TAllContext extends AnyContext = AnyContext, | ||
> = ( | ||
loaderContext: LoaderContext<TFullSearchSchema, TAllParams>, | ||
loaderContext: LoaderContext< | ||
TFullSearchSchema, | ||
TAllParams, | ||
TContext, | ||
TAllContext | ||
>, | ||
) => Promise<any> | void | ||
@@ -160,2 +187,4 @@ | ||
TAllParams extends AnyPathParams = {}, | ||
TContext extends AnyContext = AnyContext, | ||
TAllContext extends AnyContext = AnyContext, | ||
> { | ||
@@ -166,3 +195,4 @@ params: TAllParams | ||
preload: boolean | ||
// parentLoaderPromise?: Promise<TParentRouteLoaderData> | ||
routeContext: TContext | ||
context: TAllContext | ||
} | ||
@@ -180,7 +210,5 @@ | ||
TPath extends string, | ||
> = TParentRoute extends Route<any> | ||
? RootRouteId | ||
: TParentRoute extends { id: infer TParentId extends string } | ||
> = TParentRoute extends { id: infer TParentId extends string } | ||
? RoutePrefix<TParentId, string extends TCustomId ? TPath : TCustomId> | ||
: never | ||
: RootRouteId | ||
@@ -202,23 +230,26 @@ export type InferFullSearchSchema<TRoute> = TRoute extends { | ||
export type ResolveFullSearchSchema<TParentRoute, TSearchSchema> = Expand< | ||
export type ResolveFullSearchSchema<TParentRoute, TSearchSchema> = | ||
InferFullSearchSchema<TParentRoute> & TSearchSchema | ||
> | ||
export interface AnyRoute | ||
extends Route<any, any, any, any, any, any, any, any, any, any, any> {} | ||
extends Route< | ||
any, | ||
any, | ||
any, | ||
any, | ||
any, | ||
any, | ||
any, | ||
any, | ||
any, | ||
any, | ||
any, | ||
any, | ||
any, | ||
any, | ||
any, | ||
any | ||
> {} | ||
export type RouteWithRoutesInfo<TRoute> = TRoute extends Route< | ||
infer TParentRoute, | ||
infer TPath, | ||
infer TFullPath, | ||
infer TCustomId, | ||
infer TId, | ||
infer TSearchSchema, | ||
infer TFullSearchSchema, | ||
infer TParams, | ||
infer TAllParams, | ||
infer TChildren | ||
> | ||
? Route<any, any, any, any, any, any, any, any, any, TChildren, any> | ||
: never | ||
type MergeFromParent<T, U> = IsAny<T, U, T & U> | ||
@@ -247,5 +278,14 @@ export class Route< | ||
>, | ||
TAllParams extends Expand< | ||
TParentRoute['__types']['allParams'] & TParams | ||
> = Expand<TParentRoute['__types']['allParams'] & TParams>, | ||
TAllParams extends MergeFromParent< | ||
TParentRoute['__types']['allParams'], | ||
TParams | ||
> = MergeFromParent<TParentRoute['__types']['allParams'], TParams>, | ||
TParentContext extends TParentRoute['__types']['routeContext'] = TParentRoute['__types']['routeContext'], | ||
TAllParentContext extends TParentRoute['__types']['context'] = TParentRoute['__types']['context'], | ||
TRouteContext extends AnyContext = AnyContext, | ||
TContext extends MergeFromParent< | ||
TParentRoute['__types']['context'], | ||
TRouteContext | ||
> = MergeFromParent<TParentRoute['__types']['context'], TRouteContext>, | ||
TRouterContext extends AnyContext = AnyContext, | ||
TChildren extends unknown = unknown, | ||
@@ -258,3 +298,3 @@ TRoutesInfo extends DefaultRoutesInfo = DefaultRoutesInfo, | ||
fullPath: TFullPath | ||
// customId: TCustomId | ||
customId: TCustomId | ||
id: TId | ||
@@ -265,4 +305,9 @@ searchSchema: TSearchSchema | ||
allParams: TAllParams | ||
parentContext: TParentContext | ||
allParentContext: TAllParentContext | ||
routeContext: TRouteContext | ||
context: TContext | ||
children: TChildren | ||
routesInfo: TRoutesInfo | ||
routerContext: TRouterContext | ||
} | ||
@@ -276,6 +321,10 @@ isRoot: TParentRoute extends Route<any> ? true : false | ||
TSearchSchema, | ||
Expand<InferFullSearchSchema<TParentRoute> & TSearchSchema>, | ||
InferFullSearchSchema<TParentRoute> & TSearchSchema, | ||
TParentRoute['__types']['allParams'], | ||
TParams, | ||
TAllParams | ||
TAllParams, | ||
TParentContext, | ||
TAllParentContext, | ||
TRouteContext, | ||
TContext | ||
> | ||
@@ -293,3 +342,3 @@ | ||
originalIndex?: number | ||
router?: Router<TRoutesInfo['routeTree'], TRoutesInfo, unknown> | ||
router?: Router<TRoutesInfo['routeTree'], TRoutesInfo> | ||
@@ -306,3 +355,7 @@ constructor( | ||
TParams, | ||
TAllParams | ||
TAllParams, | ||
TParentContext, | ||
TAllParentContext, | ||
TRouteContext, | ||
TContext | ||
>, | ||
@@ -314,3 +367,6 @@ ) { | ||
init = () => { | ||
init = (opts: { originalIndex: number; router: AnyRouter }) => { | ||
this.originalIndex = opts.originalIndex | ||
this.router = opts.router | ||
const allOptions = this.options as RouteOptions< | ||
@@ -325,3 +381,7 @@ TParentRoute, | ||
TParams, | ||
TAllParams | ||
TAllParams, | ||
TParentContext, | ||
TAllParentContext, | ||
TRouteContext, | ||
TContext | ||
> & | ||
@@ -379,16 +439,24 @@ RouteOptionsBaseIntersection<TCustomId, TPath> | ||
addChildren = <TNewChildren extends AnyRoute[]>(children: TNewChildren) => { | ||
addChildren = <TNewChildren extends AnyRoute[]>( | ||
children: TNewChildren, | ||
): Route< | ||
TParentRoute, | ||
TPath, | ||
TFullPath, | ||
TCustomId, | ||
TId, | ||
TSearchSchema, | ||
TFullSearchSchema, | ||
TParams, | ||
TAllParams, | ||
TParentContext, | ||
TAllParentContext, | ||
TRouteContext, | ||
TContext, | ||
TRouterContext, | ||
TNewChildren, | ||
TRoutesInfo | ||
> => { | ||
this.children = children as any | ||
return this as unknown as Route< | ||
TParentRoute, | ||
TPath, | ||
TFullPath, | ||
TCustomId, | ||
TId, | ||
TSearchSchema, | ||
TFullSearchSchema, | ||
TParams, | ||
TAllParams, | ||
TNewChildren | ||
> | ||
return this as any | ||
} | ||
@@ -404,6 +472,8 @@ | ||
export type AnyRootRoute = RootRoute<any> | ||
export type AnyRootRoute = RootRoute<any, any, any> | ||
export class RootRoute< | ||
TSearchSchema extends AnySearchSchema = {}, | ||
TContext extends AnyContext = AnyContext, | ||
TRouterContext extends AnyContext = AnyContext, | ||
> extends Route< | ||
@@ -418,3 +488,8 @@ any, | ||
{}, | ||
{} | ||
{}, | ||
TRouterContext, | ||
TRouterContext, | ||
TContext, | ||
MergeFromParent<TRouterContext, TContext>, | ||
TRouterContext | ||
> { | ||
@@ -431,4 +506,6 @@ constructor( | ||
{}, | ||
{}, | ||
{} | ||
TRouterContext, | ||
TRouterContext, | ||
TContext, | ||
NoInfer<TContext> | ||
>, | ||
@@ -440,2 +517,26 @@ 'path' | 'id' | 'getParentRoute' | 'caseSensitive' | ||
} | ||
static withRouterContext = <TRouterContext extends AnyContext>() => { | ||
return < | ||
TSearchSchema extends AnySearchSchema = {}, | ||
TContext extends AnyContext = AnyContext, | ||
>( | ||
options?: Omit< | ||
RouteOptions< | ||
AnyRoute, | ||
RootRouteId, | ||
'', | ||
{}, | ||
TSearchSchema, | ||
NoInfer<TSearchSchema>, | ||
{}, | ||
TRouterContext, | ||
TRouterContext, | ||
TContext, | ||
TRouterContext & TContext | ||
>, | ||
'path' | 'id' | 'getParentRoute' | 'caseSensitive' | ||
>, | ||
) => new RootRoute<TSearchSchema, TContext, TRouterContext>(options as any) | ||
} | ||
} | ||
@@ -442,0 +543,0 @@ |
@@ -1,2 +0,2 @@ | ||
import { AnyRoute, Route } from './route' | ||
import { AnyRootRoute, AnyRoute, RootRoute, Route } from './route' | ||
import { AnyPathParams, AnySearchSchema, RootRouteId } from './route' | ||
@@ -6,3 +6,4 @@ import { IsAny, UnionToIntersection, Values } from './utils' | ||
export interface AnyRoutesInfo { | ||
routeTree: AnyRoute | ||
routeTree: AnyRootRoute | ||
routeUnion: AnyRoute | ||
routesById: Record<string, AnyRoute> | ||
@@ -12,2 +13,3 @@ routesByFullPath: Record<string, AnyRoute> | ||
routePaths: any | ||
routeIntersection: AnyRoute | ||
fullSearchSchema: Record<string, any> | ||
@@ -18,3 +20,4 @@ allParams: Record<string, any> | ||
export interface DefaultRoutesInfo { | ||
routeTree: Route | ||
routeTree: RootRoute | ||
routeUnion: AnyRoute | ||
routesById: Record<string, Route> | ||
@@ -24,2 +27,3 @@ routesByFullPath: Record<string, Route> | ||
routePaths: string | ||
routeIntersection: AnyRoute | ||
fullSearchSchema: AnySearchSchema | ||
@@ -34,8 +38,8 @@ allParams: AnyPathParams | ||
TRouteTree extends AnyRoute, | ||
TRoutes extends AnyRoute = Route, | ||
TRoutesById = { '/': TRoutes } & { | ||
[TRoute in TRoutes as TRoute['id']]: TRoute | ||
TRouteUnion extends AnyRoute = Route, | ||
TRoutesById = { '/': TRouteUnion } & { | ||
[TRoute in TRouteUnion as TRoute['id']]: TRoute | ||
}, | ||
TRoutesByFullPath = { '/': TRoutes } & { | ||
[TRoute in TRoutes as TRoute['fullPath'] extends RootRouteId | ||
TRoutesByFullPath = { '/': TRouteUnion } & { | ||
[TRoute in TRouteUnion as TRoute['fullPath'] extends RootRouteId | ||
? never | ||
@@ -48,3 +52,3 @@ : string extends TRoute['fullPath'] | ||
routeTree: TRouteTree | ||
routes: TRoutes | ||
routeUnion: TRouteUnion | ||
routesById: TRoutesById | ||
@@ -54,6 +58,24 @@ routesByFullPath: TRoutesByFullPath | ||
routePaths: keyof TRoutesByFullPath | ||
routeIntersection: Route< | ||
TRouteUnion['__types']['parentRoute'], // TParentRoute, | ||
TRouteUnion['__types']['path'], // TPath, | ||
TRouteUnion['__types']['fullPath'], // TFullPath, | ||
TRouteUnion['__types']['customId'], // TCustomId, | ||
TRouteUnion['__types']['id'], // TId, | ||
UnionToIntersection<TRouteUnion['__types']['searchSchema']> & {}, // TSearchSchema, | ||
UnionToIntersection<TRouteUnion['__types']['fullSearchSchema']> & {}, // TFullSearchSchema, | ||
UnionToIntersection<TRouteUnion['__types']['params']>, // TParams, | ||
UnionToIntersection<TRouteUnion['__types']['allParams']>, // TAllParams, | ||
UnionToIntersection<TRouteUnion['__types']['parentContext']>, // TParentContext, | ||
UnionToIntersection<TRouteUnion['__types']['allParentContext']>, // TAllParentContext, | ||
UnionToIntersection<TRouteUnion['__types']['routerContext']> & {}, // TContext, | ||
UnionToIntersection<TRouteUnion['__types']['context']> & {}, // TAllContext, | ||
UnionToIntersection<TRouteUnion['__types']['routerContext']> & {}, // TRouterContext, | ||
TRouteUnion['__types']['children'], // TChildren, | ||
TRouteUnion['__types']['routesInfo'] // TRoutesInfo, | ||
> | ||
fullSearchSchema: Partial< | ||
UnionToIntersection<TRoutes['__types']['fullSearchSchema']> | ||
UnionToIntersection<TRouteUnion['__types']['fullSearchSchema']> | ||
> | ||
allParams: Partial<UnionToIntersection<TRoutes['__types']['allParams']>> | ||
allParams: Partial<UnionToIntersection<TRouteUnion['__types']['allParams']>> | ||
} | ||
@@ -75,2 +97,7 @@ | ||
any, | ||
any, | ||
any, | ||
any, | ||
any, | ||
any, | ||
infer TChildren, | ||
@@ -77,0 +104,0 @@ any |
@@ -7,3 +7,3 @@ import { Store } from '@tanstack/store' | ||
import { AnyRouter, Router } from './router' | ||
import { Expand } from './utils' | ||
import { Expand, pick } from './utils' | ||
@@ -15,5 +15,4 @@ export interface RouteMatchStore< | ||
routeSearch: TRoute['__types']['searchSchema'] | ||
search: Expand< | ||
TRoutesInfo['fullSearchSchema'] & TRoute['__types']['fullSearchSchema'] | ||
> | ||
search: TRoutesInfo['fullSearchSchema'] & | ||
TRoute['__types']['fullSearchSchema'] | ||
status: 'idle' | 'pending' | 'success' | 'error' | ||
@@ -30,2 +29,4 @@ error?: unknown | ||
export interface AnyRouteMatch extends RouteMatch<any, any> {} | ||
export class RouteMatch< | ||
@@ -38,5 +39,8 @@ TRoutesInfo extends AnyRoutesInfo = DefaultRoutesInfo, | ||
store!: Store<RouteMatchStore<TRoutesInfo, TRoute>> | ||
state!: RouteMatchStore<TRoutesInfo, TRoute> | ||
id!: string | ||
pathname!: string | ||
params!: TRoute['__types']['allParams'] | ||
routeContext!: TRoute['__types']['routeContext'] | ||
context!: TRoute['__types']['context'] | ||
@@ -73,10 +77,19 @@ component: GetFrameworkGeneric<'Component'> | ||
params: opts.params, | ||
store: new Store<RouteMatchStore<TRoutesInfo, TRoute>>({ | ||
updatedAt: 0, | ||
routeSearch: {}, | ||
search: {} as any, | ||
status: 'idle', | ||
}), | ||
store: new Store<RouteMatchStore<TRoutesInfo, TRoute>>( | ||
{ | ||
updatedAt: 0, | ||
routeSearch: {}, | ||
search: {} as any, | ||
status: 'idle', | ||
}, | ||
{ | ||
onUpdate: (next) => { | ||
this.state = next | ||
}, | ||
}, | ||
), | ||
}) | ||
this.state = this.store.state | ||
if (!this.#hasLoaders()) { | ||
@@ -90,2 +103,74 @@ this.store.setState((s) => ({ | ||
#hasLoaders = () => { | ||
return !!( | ||
this.route.options.onLoad || | ||
componentTypes.some((d) => this.route.options[d]?.preload) | ||
) | ||
} | ||
__init = (opts: { parentMatch?: RouteMatch }) => { | ||
// Validate the search params and stabilize them | ||
this.parentMatch = opts.parentMatch | ||
const parentSearch = | ||
this.parentMatch?.state.search ?? this.router.state.latestLocation.search | ||
try { | ||
const validator = | ||
typeof this.route.options.validateSearch === 'object' | ||
? this.route.options.validateSearch.parse | ||
: this.route.options.validateSearch | ||
let nextSearch = validator?.(parentSearch) ?? {} | ||
this.store.setState((s) => ({ | ||
...s, | ||
routeSearch: nextSearch, | ||
search: { | ||
...parentSearch, | ||
...nextSearch, | ||
} as any, | ||
})) | ||
componentTypes.map(async (type) => { | ||
const component = this.route.options[type] | ||
if (typeof this[type] !== 'function') { | ||
this[type] = component | ||
} | ||
}) | ||
const parent = this.parentMatch | ||
this.routeContext = | ||
this.route.options.getContext?.({ | ||
parentContext: parent?.routeContext, | ||
context: parent?.context, | ||
params: this.params, | ||
search: this.state.search, | ||
}) || ({} as any) | ||
this.context = ( | ||
parent | ||
? { ...parent.context, ...this.routeContext } | ||
: { ...this.router?.options.context, ...this.routeContext } | ||
) as any | ||
} catch (err: any) { | ||
console.error(err) | ||
const error = new (Error as any)('Invalid search params found', { | ||
cause: err, | ||
}) | ||
error.code = 'INVALID_SEARCH_PARAMS' | ||
this.store.setState((s) => ({ | ||
...s, | ||
status: 'error', | ||
error: error, | ||
})) | ||
// Do not proceed with loading the route | ||
return | ||
} | ||
} | ||
cancel = () => { | ||
@@ -97,3 +182,3 @@ this.abortController?.abort() | ||
// If the match is invalid, errored or idle, trigger it to load | ||
if (this.store.state.status !== 'pending') { | ||
if (this.state.status !== 'pending') { | ||
await this.fetch(opts) | ||
@@ -120,3 +205,3 @@ } | ||
// as loading or resolved | ||
if (this.store.state.status === 'idle') { | ||
if (this.state.status === 'idle') { | ||
this.store.setState((s) => ({ | ||
@@ -148,5 +233,7 @@ ...s, | ||
params: this.params, | ||
search: this.store.state.search, | ||
search: this.state.search, | ||
signal: this.abortController.signal, | ||
preload: !!opts?.preload, | ||
routeContext: this.routeContext, | ||
context: this.context, | ||
}) | ||
@@ -181,63 +268,2 @@ } | ||
} | ||
#hasLoaders = () => { | ||
return !!( | ||
this.route.options.onLoad || | ||
componentTypes.some((d) => this.route.options[d]?.preload) | ||
) | ||
} | ||
__setParentMatch = (parentMatch?: RouteMatch) => { | ||
if (!this.parentMatch && parentMatch) { | ||
this.parentMatch = parentMatch | ||
} | ||
} | ||
__validate = () => { | ||
// Validate the search params and stabilize them | ||
const parentSearch = | ||
this.parentMatch?.store.state.search ?? | ||
this.router.store.state.latestLocation.search | ||
try { | ||
const validator = | ||
typeof this.route.options.validateSearch === 'object' | ||
? this.route.options.validateSearch.parse | ||
: this.route.options.validateSearch | ||
let nextSearch = validator?.(parentSearch) ?? {} | ||
this.store.setState((s) => ({ | ||
...s, | ||
routeSearch: nextSearch, | ||
search: { | ||
...parentSearch, | ||
...nextSearch, | ||
} as any, | ||
})) | ||
componentTypes.map(async (type) => { | ||
const component = this.route.options[type] | ||
if (typeof this[type] !== 'function') { | ||
this[type] = component | ||
} | ||
}) | ||
} catch (err: any) { | ||
console.error(err) | ||
const error = new (Error as any)('Invalid search params found', { | ||
cause: err, | ||
}) | ||
error.code = 'INVALID_SEARCH_PARAMS' | ||
this.store.setState((s) => ({ | ||
...s, | ||
status: 'error', | ||
error: error, | ||
})) | ||
// Do not proceed with loading the route | ||
return | ||
} | ||
} | ||
} |
@@ -27,10 +27,11 @@ import { Store } from '@tanstack/store' | ||
AnyPathParams, | ||
AnyRoute, | ||
AnySearchSchema, | ||
LoaderContext, | ||
SearchFilter, | ||
AnyRootRoute, | ||
AnyRoute, | ||
RootRoute, | ||
AnyContext, | ||
} from './route' | ||
import { RoutesInfo, AnyRoutesInfo, RoutesById } from './routeInfo' | ||
import { RouteMatch, RouteMatchStore } from './routeMatch' | ||
import { AnyRouteMatch, RouteMatch, RouteMatchStore } from './routeMatch' | ||
import { defaultParseSearch, defaultStringifySearch } from './searchParams' | ||
@@ -58,12 +59,12 @@ import { | ||
export type AnyRouter = Router<any, any, any> | ||
export type AnyRouter = Router<any, any> | ||
export type RegisteredRouter = Register extends { | ||
router: Router<infer TRoute, infer TRoutesInfo, infer TRouterContext> | ||
router: Router<infer TRoute, infer TRoutesInfo> | ||
} | ||
? Router<TRoute, TRoutesInfo, TRouterContext> | ||
? Router<TRoute, TRoutesInfo> | ||
: Router | ||
export type RegisteredRoutesInfo = Register extends { | ||
router: Router<infer TRoute, infer TRoutesInfo, infer TRouterContext> | ||
router: Router<infer TRoute, infer TRoutesInfo> | ||
} | ||
@@ -101,6 +102,12 @@ ? TRoutesInfo | ||
export interface RouterOptions< | ||
TRouteTree extends AnyRootRoute, | ||
TRouterContext, | ||
> { | ||
type RouterContextOptions<TRouteTree extends AnyRoute> = | ||
AnyContext extends TRouteTree['__types']['routerContext'] | ||
? { | ||
context?: TRouteTree['__types']['routerContext'] | ||
} | ||
: { | ||
context: TRouteTree['__types']['routerContext'] | ||
} | ||
export interface RouterOptions<TRouteTree extends AnyRoute> { | ||
history?: RouterHistory | ||
@@ -122,3 +129,2 @@ stringifySearch?: SearchSerializer | ||
createRoute?: (opts: { route: AnyRoute; router: AnyRouter }) => void | ||
context?: TRouterContext | ||
loadComponent?: ( | ||
@@ -129,2 +135,3 @@ component: GetFrameworkGeneric<'Component'>, | ||
fetchServerDataFn?: FetchServerDataFn | ||
context?: TRouteTree['__types']['routerContext'] | ||
} | ||
@@ -146,11 +153,11 @@ | ||
export interface RouterStore< | ||
TSearchObj extends AnySearchSchema = {}, | ||
TRoutesInfo extends AnyRoutesInfo = AnyRoutesInfo, | ||
TState extends LocationState = LocationState, | ||
> { | ||
status: 'idle' | 'pending' | ||
latestLocation: ParsedLocation<TSearchObj, TState> | ||
currentMatches: RouteMatch[] | ||
currentLocation: ParsedLocation<TSearchObj, TState> | ||
pendingMatches?: RouteMatch[] | ||
pendingLocation?: ParsedLocation<TSearchObj, TState> | ||
latestLocation: ParsedLocation<TRoutesInfo['fullSearchSchema'], TState> | ||
currentMatches: RouteMatch<TRoutesInfo, TRoutesInfo['routeIntersection']>[] | ||
currentLocation: ParsedLocation<TRoutesInfo['fullSearchSchema'], TState> | ||
pendingMatches?: RouteMatch<TRoutesInfo, TRoutesInfo['routeIntersection']>[] | ||
pendingLocation?: ParsedLocation<TRoutesInfo['fullSearchSchema'], TState> | ||
lastUpdated: number | ||
@@ -205,6 +212,4 @@ } | ||
export interface DehydratedRouter<TRouterContext = unknown> { | ||
// location: Router['__location'] | ||
export interface DehydratedRouter { | ||
state: DehydratedRouterState | ||
context: TRouterContext | ||
} | ||
@@ -248,5 +253,4 @@ | ||
export class Router< | ||
TRouteTree extends AnyRoute = Route, | ||
TRouteTree extends AnyRoute = RootRoute, | ||
TRoutesInfo extends AnyRoutesInfo = RoutesInfo<TRouteTree>, | ||
TRouterContext = unknown, | ||
> { | ||
@@ -260,5 +264,6 @@ types!: { | ||
options: PickAsRequired< | ||
RouterOptions<TRouteTree, TRouterContext>, | ||
'stringifySearch' | 'parseSearch' | 'context' | ||
> | ||
Omit<RouterOptions<TRouteTree>, 'context'>, | ||
'stringifySearch' | 'parseSearch' | ||
> & | ||
RouterContextOptions<TRouteTree> | ||
history!: RouterHistory | ||
@@ -268,3 +273,3 @@ #unsubHistory?: () => void | ||
// __location: Location<TRoutesInfo['fullSearchSchema']> | ||
routeTree!: Route | ||
routeTree!: RootRoute | ||
routesById!: RoutesById<TRoutesInfo> | ||
@@ -275,7 +280,8 @@ navigateTimeout: undefined | Timeout | ||
store: Store<RouterStore<TRoutesInfo['fullSearchSchema']>> | ||
store: Store<RouterStore<TRoutesInfo>> | ||
state: RouterStore<TRoutesInfo> | ||
startedLoadingAt = Date.now() | ||
resolveNavigation = () => {} | ||
constructor(options?: RouterOptions<TRouteTree, TRouterContext>) { | ||
constructor(options?: RouterOptions<TRouteTree>) { | ||
this.options = { | ||
@@ -290,3 +296,8 @@ defaultPreloadDelay: 50, | ||
this.store = new Store(getInitialRouterState()) | ||
this.store = new Store<RouterStore<TRoutesInfo>>(getInitialRouterState(), { | ||
onUpdate: (state) => { | ||
this.state = state | ||
}, | ||
}) | ||
this.state = this.store.state | ||
this.basepath = '' | ||
@@ -308,3 +319,3 @@ | ||
// If the router matches are empty, load the matches | ||
if (!this.store.state.currentMatches.length) { | ||
if (!this.state.currentMatches.length) { | ||
this.load() | ||
@@ -338,9 +349,3 @@ } | ||
update = < | ||
TRoute extends AnyRootRoute = AnyRootRoute, | ||
TRoutesInfo extends AnyRoutesInfo = RoutesInfo<TRoute>, | ||
TRouterContext = unknown, | ||
>( | ||
opts?: RouterOptions<TRoute, TRouterContext>, | ||
): this => { | ||
update = (opts?: RouterOptions<TRouteTree>): this => { | ||
Object.assign(this.options, opts) | ||
@@ -369,3 +374,5 @@ | ||
this.#unsubHistory = this.history.listen(() => { | ||
this.load(this.#parseLocation(this.store.state.latestLocation)) | ||
this.load({ | ||
next: this.#parseLocation(this.state.latestLocation), | ||
}) | ||
}) | ||
@@ -383,3 +390,3 @@ } | ||
return this as any | ||
return this | ||
} | ||
@@ -411,4 +418,4 @@ | ||
;[ | ||
...this.store.state.currentMatches, | ||
...(this.store.state.pendingMatches || []), | ||
...this.state.currentMatches, | ||
...(this.state.pendingMatches || []), | ||
].forEach((match) => { | ||
@@ -419,3 +426,6 @@ match.cancel() | ||
load = async (next?: ParsedLocation) => { | ||
load = async (opts?: { | ||
next?: ParsedLocation | ||
// filter?: (match: RouteMatch<any, any>) => any | ||
}) => { | ||
let now = Date.now() | ||
@@ -431,7 +441,7 @@ const startedAt = now | ||
this.store.batch(() => { | ||
if (next) { | ||
if (opts?.next) { | ||
// Ingest the new location | ||
this.store.setState((s) => ({ | ||
...s, | ||
latestLocation: next, | ||
latestLocation: opts.next!, | ||
})) | ||
@@ -441,3 +451,3 @@ } | ||
// Match the routes | ||
matches = this.matchRoutes(this.store.state.latestLocation.pathname, { | ||
matches = this.matchRoutes(this.state.latestLocation.pathname, { | ||
strictParseParams: true, | ||
@@ -450,3 +460,3 @@ }) | ||
pendingMatches: matches, | ||
pendingLocation: this.store.state.latestLocation, | ||
pendingLocation: this.state.latestLocation, | ||
})) | ||
@@ -457,3 +467,6 @@ }) | ||
try { | ||
await this.loadMatches(matches) | ||
await this.loadMatches( | ||
matches, | ||
// opts | ||
) | ||
} catch (err: any) { | ||
@@ -472,6 +485,6 @@ console.warn(err) | ||
const previousMatches = this.store.state.currentMatches | ||
const previousMatches = this.state.currentMatches | ||
const exiting: RouteMatch[] = [], | ||
staying: RouteMatch[] = [] | ||
const exiting: AnyRouteMatch[] = [], | ||
staying: AnyRouteMatch[] = [] | ||
@@ -495,7 +508,7 @@ previousMatches.forEach((d) => { | ||
params: d.params, | ||
search: d.store.state.routeSearch, | ||
search: d.state.routeSearch, | ||
}) | ||
// Clear non-loading error states when match leaves | ||
if (d.store.state.status === 'error') { | ||
if (d.state.status === 'error') { | ||
this.store.setState((s) => ({ | ||
@@ -512,3 +525,3 @@ ...s, | ||
params: d.params, | ||
search: d.store.state.routeSearch, | ||
search: d.state.routeSearch, | ||
}) | ||
@@ -520,5 +533,4 @@ }) | ||
params: d.params, | ||
search: d.store.state.search, | ||
search: d.state.search, | ||
}) | ||
// delete this.store.state.matchCache[d.id] // TODO: | ||
}) | ||
@@ -529,3 +541,3 @@ | ||
status: 'idle', | ||
currentLocation: this.store.state.latestLocation, | ||
currentLocation: this.state.latestLocation, | ||
currentMatches: matches, | ||
@@ -552,3 +564,3 @@ pendingLocation: undefined, | ||
loadRoute = async ( | ||
navigateOpts: BuildNextOptions = this.store.state.latestLocation, | ||
navigateOpts: BuildNextOptions = this.state.latestLocation, | ||
): Promise<RouteMatch[]> => { | ||
@@ -564,3 +576,3 @@ const next = this.buildNext(navigateOpts) | ||
preloadRoute = async ( | ||
navigateOpts: BuildNextOptions = this.store.state.latestLocation, | ||
navigateOpts: BuildNextOptions = this.state.latestLocation, | ||
) => { | ||
@@ -579,3 +591,3 @@ const next = this.buildNext(navigateOpts) | ||
matchRoutes = (pathname: string, opts?: { strictParseParams?: boolean }) => { | ||
const matches: RouteMatch[] = [] | ||
const matches: AnyRouteMatch[] = [] | ||
@@ -587,4 +599,4 @@ if (!this.routeTree) { | ||
const existingMatches = [ | ||
...this.store.state.currentMatches, | ||
...(this.store.state.pendingMatches ?? []), | ||
...this.state.currentMatches, | ||
...(this.state.pendingMatches ?? []), | ||
] | ||
@@ -661,3 +673,2 @@ | ||
existingMatches.find((d) => d.id === matchId) || | ||
// this.store.state.matchCache[matchId]?.match || // TODO: | ||
new RouteMatch(this, foundRoute, { | ||
@@ -688,12 +699,9 @@ id: matchId, | ||
resolvedMatches: RouteMatch[], | ||
loaderOpts?: { preload?: boolean }, | ||
opts?: { | ||
preload?: boolean | ||
// filter?: (match: RouteMatch<any, any>) => any | ||
}, | ||
) => { | ||
linkMatches(resolvedMatches) | ||
initMatches(resolvedMatches) | ||
// this.cleanMatchCache() | ||
resolvedMatches.forEach(async (match) => { | ||
// Validate the match (loads search params etc) | ||
match.__validate() | ||
}) | ||
// Check each match middleware to see if the route can be accessed | ||
@@ -708,3 +716,3 @@ await Promise.all( | ||
} catch (err) { | ||
if (!loaderOpts?.preload) { | ||
if (!opts?.preload) { | ||
match.route.options.onLoadError?.(err) | ||
@@ -720,11 +728,11 @@ } | ||
const prevMatch = resolvedMatches[(index = 1)] | ||
const search = match.store.state.search as { __data?: any } | ||
const search = match.state.search as { __data?: any } | ||
if (search.__data?.matchId && search.__data.matchId !== match.id) { | ||
return | ||
} | ||
// if (opts?.filter && !opts.filter(match)) { | ||
// return | ||
// } | ||
match.load({ preload: loaderOpts?.preload }) | ||
match.load({ preload: opts?.preload }) | ||
if (match.store.state.status !== 'success' && match.__loadPromise) { | ||
if (match.state.status !== 'success' && match.__loadPromise) { | ||
// Wait for the first sign of activity from the match | ||
@@ -812,3 +820,3 @@ await match.__loadPromise | ||
if (opts?.pending) { | ||
if (!this.store.state.pendingLocation) { | ||
if (!this.state.pendingLocation) { | ||
return false | ||
@@ -819,3 +827,3 @@ } | ||
this.basepath, | ||
this.store.state.pendingLocation!.pathname, | ||
this.state.pendingLocation!.pathname, | ||
{ | ||
@@ -828,10 +836,6 @@ ...opts, | ||
return matchPathname( | ||
this.basepath, | ||
this.store.state.currentLocation.pathname, | ||
{ | ||
...opts, | ||
to: next.pathname, | ||
}, | ||
) as any | ||
return matchPathname(this.basepath, this.state.currentLocation.pathname, { | ||
...opts, | ||
to: next.pathname, | ||
}) as any | ||
} | ||
@@ -887,6 +891,4 @@ | ||
// Compare path/hash for matches | ||
const pathIsEqual = | ||
this.store.state.currentLocation.pathname === next.pathname | ||
const currentPathSplit = | ||
this.store.state.currentLocation.pathname.split('/') | ||
const pathIsEqual = this.state.currentLocation.pathname === next.pathname | ||
const currentPathSplit = this.state.currentLocation.pathname.split('/') | ||
const nextPathSplit = next.pathname.split('/') | ||
@@ -896,3 +898,3 @@ const pathIsFuzzyEqual = nextPathSplit.every( | ||
) | ||
const hashIsEqual = this.store.state.currentLocation.hash === next.hash | ||
const hashIsEqual = this.state.currentLocation.hash === next.hash | ||
// Combine the matches based on user options | ||
@@ -970,6 +972,6 @@ const pathTest = activeOptions?.exact ? pathIsEqual : pathIsFuzzyEqual | ||
dehydrate = (): DehydratedRouter<TRouterContext> => { | ||
dehydrate = (): DehydratedRouter => { | ||
return { | ||
state: { | ||
...pick(this.store.state, [ | ||
...pick(this.state, [ | ||
'latestLocation', | ||
@@ -980,17 +982,14 @@ 'currentLocation', | ||
]), | ||
currentMatches: this.store.state.currentMatches.map((match) => ({ | ||
currentMatches: this.state.currentMatches.map((match) => ({ | ||
id: match.id, | ||
state: { | ||
...pick(match.store.state, ['status']), | ||
status: match.state.status, | ||
}, | ||
})), | ||
}, | ||
context: this.options.context as TRouterContext, | ||
} | ||
} | ||
hydrate = (dehydratedRouter: DehydratedRouter<TRouterContext>) => { | ||
hydrate = (dehydratedRouter: DehydratedRouter) => { | ||
this.store.setState((s) => { | ||
this.options.context = dehydratedRouter.context | ||
// Match the routes | ||
@@ -1016,3 +1015,3 @@ const currentMatches = this.matchRoutes( | ||
currentMatches.forEach((match) => match.__validate()) | ||
initMatches(currentMatches) | ||
@@ -1027,10 +1026,7 @@ return { | ||
#buildRouteTree = (routeTree: Route) => { | ||
#buildRouteTree = (routeTree: AnyRoute) => { | ||
const recurseRoutes = (routes: Route[]) => { | ||
routes.forEach((route, i) => { | ||
route.init() | ||
route.init({ originalIndex: i, router: this }) | ||
route.originalIndex = i | ||
route.router = this as any | ||
const existingRoute = (this.routesById as any)[route.id] | ||
@@ -1057,3 +1053,3 @@ | ||
recurseRoutes([routeTree]) | ||
recurseRoutes([routeTree] as Route[]) | ||
@@ -1085,4 +1081,4 @@ return routeTree | ||
const fromPathname = dest.fromCurrent | ||
? this.store.state.latestLocation.pathname | ||
: dest.from ?? this.store.state.latestLocation.pathname | ||
? this.state.latestLocation.pathname | ||
: dest.from ?? this.state.latestLocation.pathname | ||
@@ -1095,8 +1091,5 @@ let pathname = resolvePath( | ||
const fromMatches = this.matchRoutes( | ||
this.store.state.latestLocation.pathname, | ||
{ | ||
strictParseParams: true, | ||
}, | ||
) | ||
const fromMatches = this.matchRoutes(this.state.latestLocation.pathname, { | ||
strictParseParams: true, | ||
}) | ||
@@ -1127,5 +1120,5 @@ const toMatches = this.matchRoutes(pathname) | ||
(prev, next) => next(prev), | ||
this.store.state.latestLocation.search, | ||
this.state.latestLocation.search, | ||
) | ||
: this.store.state.latestLocation.search | ||
: this.state.latestLocation.search | ||
@@ -1148,3 +1141,3 @@ // Then the link/navigate function | ||
const search = replaceEqualDeep( | ||
this.store.state.latestLocation.search, | ||
this.state.latestLocation.search, | ||
postFilteredSearch, | ||
@@ -1156,4 +1149,4 @@ ) | ||
dest.hash === true | ||
? this.store.state.latestLocation.hash | ||
: functionalUpdate(dest.hash!, this.store.state.latestLocation.hash) | ||
? this.state.latestLocation.hash | ||
: functionalUpdate(dest.hash!, this.state.latestLocation.hash) | ||
hash = hash ? `#${hash}` : '' | ||
@@ -1165,3 +1158,3 @@ | ||
searchStr, | ||
state: this.store.state.latestLocation.state, | ||
state: this.state.latestLocation.state, | ||
hash, | ||
@@ -1185,3 +1178,3 @@ href: `${pathname}${searchStr}${hash}`, | ||
const isSameUrl = this.store.state.latestLocation.href === next.href | ||
const isSameUrl = this.state.latestLocation.href === next.href | ||
@@ -1201,3 +1194,3 @@ if (isSameUrl && !next.key) { | ||
// this.load(this.#parseLocation(this.store.state.latestLocation)) | ||
// this.load(this.#parseLocation(this.state.latestLocation)) | ||
@@ -1218,3 +1211,3 @@ return (this.navigationPromise = new Promise((resolve) => { | ||
function getInitialRouterState(): RouterStore { | ||
function getInitialRouterState(): RouterStore<any, any> { | ||
return { | ||
@@ -1233,10 +1226,7 @@ status: 'idle', | ||
function linkMatches(matches: RouteMatch<any, any>[]) { | ||
function initMatches(matches: RouteMatch<any, any>[]) { | ||
matches.forEach((match, index) => { | ||
const parent = matches[index - 1] | ||
if (parent) { | ||
match.__setParentMatch(parent) | ||
} | ||
const parentMatch = matches[index - 1] | ||
match.__init({ parentMatch }) | ||
}) | ||
} |
@@ -10,3 +10,3 @@ export type NoInfer<T> = [T][T extends any ? 0 : never] | ||
export type PickUnsafe<T, K> = K extends keyof T ? Pick<T, K> : never | ||
export type PickExtra<T, K> = Expand<{ | ||
export type PickExtra<T, K> = { | ||
[TKey in keyof K as string extends TKey | ||
@@ -17,3 +17,4 @@ ? never | ||
: TKey]: K[TKey] | ||
}> | ||
} | ||
export type PickRequired<T> = { | ||
@@ -20,0 +21,0 @@ [K in keyof T as undefined extends T[K] ? never : K]: T[K] |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
930684
8493
+ Added@tanstack/store@0.0.1-beta.62(transitive)
- Removed@tanstack/store@0.0.1-beta.57(transitive)