@tanstack/router-core 0.0.1-beta.19 → 0.0.1-beta.191

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/LICENSE +21 -0
  2. package/build/cjs/defer.js +39 -0
  3. package/build/cjs/defer.js.map +1 -0
  4. package/build/cjs/fileRoute.js +29 -0
  5. package/build/cjs/fileRoute.js.map +1 -0
  6. package/build/cjs/history.js +228 -0
  7. package/build/cjs/history.js.map +1 -0
  8. package/build/cjs/index.js +86 -0
  9. package/build/cjs/{packages/router-core/src/index.js.map → index.js.map} +1 -1
  10. package/build/cjs/{packages/router-core/src/path.js → path.js} +45 -56
  11. package/build/cjs/path.js.map +1 -0
  12. package/build/cjs/{packages/router-core/src/qss.js → qss.js} +10 -16
  13. package/build/cjs/qss.js.map +1 -0
  14. package/build/cjs/route.js +114 -0
  15. package/build/cjs/route.js.map +1 -0
  16. package/build/cjs/router.js +1267 -0
  17. package/build/cjs/router.js.map +1 -0
  18. package/build/cjs/scroll-restoration.js +139 -0
  19. package/build/cjs/scroll-restoration.js.map +1 -0
  20. package/build/cjs/{packages/router-core/src/searchParams.js → searchParams.js} +32 -19
  21. package/build/cjs/searchParams.js.map +1 -0
  22. package/build/cjs/{packages/router-core/src/utils.js → utils.js} +69 -64
  23. package/build/cjs/utils.js.map +1 -0
  24. package/build/esm/index.js +1746 -2121
  25. package/build/esm/index.js.map +1 -1
  26. package/build/stats-html.html +59 -49
  27. package/build/stats-react.json +197 -211
  28. package/build/types/defer.d.ts +19 -0
  29. package/build/types/fileRoute.d.ts +35 -0
  30. package/build/types/history.d.ts +36 -0
  31. package/build/types/index.d.ts +13 -609
  32. package/build/types/link.d.ts +96 -0
  33. package/build/types/path.d.ts +16 -0
  34. package/build/types/qss.d.ts +2 -0
  35. package/build/types/route.d.ts +251 -0
  36. package/build/types/routeInfo.d.ts +22 -0
  37. package/build/types/router.d.ts +260 -0
  38. package/build/types/scroll-restoration.d.ts +6 -0
  39. package/build/types/searchParams.d.ts +5 -0
  40. package/build/types/utils.d.ts +44 -0
  41. package/build/umd/index.development.js +1978 -2243
  42. package/build/umd/index.development.js.map +1 -1
  43. package/build/umd/index.production.js +13 -2
  44. package/build/umd/index.production.js.map +1 -1
  45. package/package.json +11 -7
  46. package/src/defer.ts +55 -0
  47. package/src/fileRoute.ts +161 -0
  48. package/src/history.ts +300 -0
  49. package/src/index.ts +5 -10
  50. package/src/link.ts +136 -125
  51. package/src/path.ts +37 -17
  52. package/src/qss.ts +1 -2
  53. package/src/route.ts +948 -218
  54. package/src/routeInfo.ts +45 -211
  55. package/src/router.ts +1778 -1075
  56. package/src/scroll-restoration.ts +179 -0
  57. package/src/searchParams.ts +31 -9
  58. package/src/utils.ts +84 -49
  59. package/build/cjs/_virtual/_rollupPluginBabelHelpers.js +0 -33
  60. package/build/cjs/_virtual/_rollupPluginBabelHelpers.js.map +0 -1
  61. package/build/cjs/node_modules/@babel/runtime/helpers/esm/extends.js +0 -33
  62. package/build/cjs/node_modules/@babel/runtime/helpers/esm/extends.js.map +0 -1
  63. package/build/cjs/node_modules/history/index.js +0 -815
  64. package/build/cjs/node_modules/history/index.js.map +0 -1
  65. package/build/cjs/node_modules/tiny-invariant/dist/esm/tiny-invariant.js +0 -30
  66. package/build/cjs/node_modules/tiny-invariant/dist/esm/tiny-invariant.js.map +0 -1
  67. package/build/cjs/packages/router-core/src/index.js +0 -58
  68. package/build/cjs/packages/router-core/src/path.js.map +0 -1
  69. package/build/cjs/packages/router-core/src/qss.js.map +0 -1
  70. package/build/cjs/packages/router-core/src/route.js +0 -147
  71. package/build/cjs/packages/router-core/src/route.js.map +0 -1
  72. package/build/cjs/packages/router-core/src/routeConfig.js +0 -69
  73. package/build/cjs/packages/router-core/src/routeConfig.js.map +0 -1
  74. package/build/cjs/packages/router-core/src/routeMatch.js +0 -220
  75. package/build/cjs/packages/router-core/src/routeMatch.js.map +0 -1
  76. package/build/cjs/packages/router-core/src/router.js +0 -870
  77. package/build/cjs/packages/router-core/src/router.js.map +0 -1
  78. package/build/cjs/packages/router-core/src/searchParams.js.map +0 -1
  79. package/build/cjs/packages/router-core/src/utils.js.map +0 -1
  80. package/src/frameworks.ts +0 -11
  81. package/src/routeConfig.ts +0 -511
  82. package/src/routeMatch.ts +0 -312
@@ -0,0 +1,1267 @@
1
+ /**
2
+ * @tanstack/router-core/src/index.ts
3
+ *
4
+ * Copyright (c) TanStack
5
+ *
6
+ * This source code is licensed under the MIT license found in the
7
+ * LICENSE.md file in the root directory of this source tree.
8
+ *
9
+ * @license MIT
10
+ */
11
+ 'use strict';
12
+
13
+ Object.defineProperty(exports, '__esModule', { value: true });
14
+
15
+ var store = require('@tanstack/store');
16
+ var invariant = require('tiny-invariant');
17
+ var path = require('./path.js');
18
+ var searchParams = require('./searchParams.js');
19
+ var utils = require('./utils.js');
20
+ var history = require('./history.js');
21
+
22
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
23
+
24
+ var invariant__default = /*#__PURE__*/_interopDefaultLegacy(invariant);
25
+
26
+ //
27
+
28
+ const componentTypes = ['component', 'errorComponent', 'pendingComponent'];
29
+ const visibilityChangeEvent = 'visibilitychange';
30
+ const focusEvent = 'focus';
31
+ const preloadWarning = 'Error preloading route! ☝️';
32
+ class Router {
33
+ #unsubHistory;
34
+ resetNextScroll = false;
35
+ tempLocationKey = `${Math.round(Math.random() * 10000000)}`;
36
+ // nextTemporaryLocation?: ParsedLocation<FullSearchSchema<TRouteTree>>
37
+
38
+ constructor(options) {
39
+ this.options = {
40
+ defaultPreloadDelay: 50,
41
+ context: undefined,
42
+ ...options,
43
+ stringifySearch: options?.stringifySearch ?? searchParams.defaultStringifySearch,
44
+ parseSearch: options?.parseSearch ?? searchParams.defaultParseSearch
45
+ // fetchServerDataFn: options?.fetchServerDataFn ?? defaultFetchServerDataFn,
46
+ };
47
+
48
+ this.__store = new store.Store(getInitialRouterState(), {
49
+ onUpdate: () => {
50
+ const prev = this.state;
51
+ const next = this.__store.state;
52
+ const matchesByIdChanged = prev.matchesById !== next.matchesById;
53
+ let matchesChanged;
54
+ let pendingMatchesChanged;
55
+ if (!matchesByIdChanged) {
56
+ matchesChanged = prev.matchIds.length !== next.matchIds.length || prev.matchIds.some((d, i) => d !== next.matchIds[i]);
57
+ pendingMatchesChanged = prev.pendingMatchIds.length !== next.pendingMatchIds.length || prev.pendingMatchIds.some((d, i) => d !== next.pendingMatchIds[i]);
58
+ }
59
+ if (matchesByIdChanged || matchesChanged) {
60
+ next.matches = next.matchIds.map(id => {
61
+ return next.matchesById[id];
62
+ });
63
+ }
64
+ if (matchesByIdChanged || pendingMatchesChanged) {
65
+ next.pendingMatches = next.pendingMatchIds.map(id => {
66
+ return next.matchesById[id];
67
+ });
68
+ }
69
+ if (matchesByIdChanged || matchesChanged || pendingMatchesChanged) {
70
+ const hasPendingComponent = next.pendingMatches.some(d => {
71
+ const route = this.getRoute(d.routeId);
72
+ return !!route?.options.pendingComponent;
73
+ });
74
+ next.renderedMatchIds = hasPendingComponent ? next.pendingMatchIds : next.matchIds;
75
+ next.renderedMatches = next.renderedMatchIds.map(id => {
76
+ return next.matchesById[id];
77
+ });
78
+ }
79
+ next.isFetching = [...next.matches, ...next.pendingMatches].some(d => d.isFetching);
80
+ this.state = next;
81
+ },
82
+ defaultPriority: 'low'
83
+ });
84
+ this.state = this.__store.state;
85
+ this.update(options);
86
+ const nextLocation = this.buildLocation({
87
+ search: true,
88
+ params: true,
89
+ hash: true,
90
+ state: true
91
+ });
92
+ if (this.state.location.href !== nextLocation.href) {
93
+ this.#commitLocation({
94
+ ...nextLocation,
95
+ replace: true
96
+ });
97
+ }
98
+ }
99
+ subscribers = new Set();
100
+ subscribe = (eventType, fn) => {
101
+ const listener = {
102
+ eventType,
103
+ fn
104
+ };
105
+ this.subscribers.add(listener);
106
+ return () => {
107
+ this.subscribers.delete(listener);
108
+ };
109
+ };
110
+ #emit = routerEvent => {
111
+ this.subscribers.forEach(listener => {
112
+ if (listener.eventType === routerEvent.type) {
113
+ listener.fn(routerEvent);
114
+ }
115
+ });
116
+ };
117
+ reset = () => {
118
+ this.__store.setState(s => Object.assign(s, getInitialRouterState()));
119
+ };
120
+ mount = () => {
121
+ // addEventListener does not exist in React Native, but window does
122
+ // In the future, we might need to invert control here for more adapters
123
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
124
+ if (typeof window !== 'undefined' && window.addEventListener) {
125
+ window.addEventListener(visibilityChangeEvent, this.#onFocus, false);
126
+ window.addEventListener(focusEvent, this.#onFocus, false);
127
+ }
128
+ this.safeLoad();
129
+ return () => {
130
+ if (typeof window !== 'undefined' && window.removeEventListener) {
131
+ window.removeEventListener(visibilityChangeEvent, this.#onFocus);
132
+ window.removeEventListener(focusEvent, this.#onFocus);
133
+ }
134
+ };
135
+ };
136
+ #onFocus = () => {
137
+ if (this.options.reloadOnWindowFocus ?? true) {
138
+ this.invalidate({
139
+ __fromFocus: true
140
+ });
141
+ }
142
+ };
143
+ update = opts => {
144
+ this.options = {
145
+ ...this.options,
146
+ ...opts,
147
+ context: {
148
+ ...this.options.context,
149
+ ...opts?.context
150
+ }
151
+ };
152
+ if (!this.history || this.options.history && this.options.history !== this.history) {
153
+ if (this.#unsubHistory) {
154
+ this.#unsubHistory();
155
+ }
156
+ this.history = this.options.history ?? (isServer ? history.createMemoryHistory() : history.createBrowserHistory());
157
+ const parsedLocation = this.#parseLocation();
158
+ this.__store.setState(s => ({
159
+ ...s,
160
+ resolvedLocation: parsedLocation,
161
+ location: parsedLocation
162
+ }));
163
+ this.#unsubHistory = this.history.subscribe(() => {
164
+ this.safeLoad({
165
+ next: this.#parseLocation(this.state.location)
166
+ });
167
+ });
168
+ }
169
+ const {
170
+ basepath,
171
+ routeTree
172
+ } = this.options;
173
+ this.basepath = `/${path.trimPath(basepath ?? '') ?? ''}`;
174
+ if (routeTree && routeTree !== this.routeTree) {
175
+ this.#processRoutes(routeTree);
176
+ }
177
+ return this;
178
+ };
179
+ cancelMatches = () => {
180
+ this.state.matches.forEach(match => {
181
+ this.cancelMatch(match.id);
182
+ });
183
+ };
184
+ cancelMatch = id => {
185
+ this.getRouteMatch(id)?.abortController?.abort();
186
+ };
187
+ safeLoad = async opts => {
188
+ try {
189
+ return this.load(opts);
190
+ } catch (err) {
191
+ // Don't do anything
192
+ }
193
+ };
194
+ latestLoadPromise = Promise.resolve();
195
+ load = async opts => {
196
+ const promise = new Promise(async (resolve, reject) => {
197
+ const prevLocation = this.state.resolvedLocation;
198
+ const pathDidChange = !!(opts?.next && prevLocation.href !== opts.next.href);
199
+ let latestPromise;
200
+ const checkLatest = () => {
201
+ return this.latestLoadPromise !== promise ? this.latestLoadPromise : undefined;
202
+ };
203
+
204
+ // Cancel any pending matches
205
+
206
+ let pendingMatches;
207
+ this.#emit({
208
+ type: 'onBeforeLoad',
209
+ from: prevLocation,
210
+ to: opts?.next ?? this.state.location,
211
+ pathChanged: pathDidChange
212
+ });
213
+ this.__store.batch(() => {
214
+ if (opts?.next) {
215
+ // Ingest the new location
216
+ this.__store.setState(s => ({
217
+ ...s,
218
+ location: opts.next
219
+ }));
220
+ }
221
+
222
+ // Match the routes
223
+ pendingMatches = this.matchRoutes(this.state.location.pathname, this.state.location.search, {
224
+ throwOnError: opts?.throwOnError,
225
+ debug: true
226
+ });
227
+ this.__store.setState(s => ({
228
+ ...s,
229
+ status: 'pending',
230
+ pendingMatchIds: pendingMatches.map(d => d.id),
231
+ matchesById: this.#mergeMatches(s.matchesById, pendingMatches)
232
+ }));
233
+ });
234
+ try {
235
+ // Load the matches
236
+ try {
237
+ await this.loadMatches(pendingMatches.map(d => d.id));
238
+ } catch (err) {
239
+ // swallow this error, since we'll display the
240
+ // errors on the route components
241
+ }
242
+
243
+ // Only apply the latest transition
244
+ if (latestPromise = checkLatest()) {
245
+ return latestPromise;
246
+ }
247
+ const exitingMatchIds = this.state.matchIds.filter(id => !this.state.pendingMatchIds.includes(id));
248
+ const enteringMatchIds = this.state.pendingMatchIds.filter(id => !this.state.matchIds.includes(id));
249
+ const stayingMatchIds = this.state.matchIds.filter(id => this.state.pendingMatchIds.includes(id));
250
+ this.__store.setState(s => ({
251
+ ...s,
252
+ status: 'idle',
253
+ resolvedLocation: s.location,
254
+ matchIds: s.pendingMatchIds,
255
+ pendingMatchIds: []
256
+ }));
257
+ [[exitingMatchIds, 'onLeave'], [enteringMatchIds, 'onEnter'], [stayingMatchIds, 'onTransition']].forEach(([matchIds, hook]) => {
258
+ matchIds.forEach(id => {
259
+ const match = this.getRouteMatch(id);
260
+ const route = this.getRoute(match.routeId);
261
+ route.options[hook]?.(match);
262
+ });
263
+ });
264
+ this.#emit({
265
+ type: 'onLoad',
266
+ from: prevLocation,
267
+ to: this.state.location,
268
+ pathChanged: pathDidChange
269
+ });
270
+ resolve();
271
+ } catch (err) {
272
+ // Only apply the latest transition
273
+ if (latestPromise = checkLatest()) {
274
+ return latestPromise;
275
+ }
276
+ reject(err);
277
+ }
278
+ });
279
+ this.latestLoadPromise = promise;
280
+ this.latestLoadPromise.then(() => {
281
+ this.cleanMatches();
282
+ });
283
+ return this.latestLoadPromise;
284
+ };
285
+ #mergeMatches = (prevMatchesById, nextMatches) => {
286
+ let matchesById = {
287
+ ...prevMatchesById
288
+ };
289
+ nextMatches.forEach(match => {
290
+ if (!matchesById[match.id]) {
291
+ matchesById[match.id] = match;
292
+ }
293
+ matchesById[match.id] = {
294
+ ...matchesById[match.id],
295
+ ...match
296
+ };
297
+ });
298
+ return matchesById;
299
+ };
300
+ getRoute = id => {
301
+ const route = this.routesById[id];
302
+ invariant__default["default"](route, `Route with id "${id}" not found`);
303
+ return route;
304
+ };
305
+ preloadRoute = async (navigateOpts = this.state.location) => {
306
+ let next = this.buildLocation(navigateOpts);
307
+ const matches = this.matchRoutes(next.pathname, next.search, {
308
+ throwOnError: true
309
+ });
310
+ this.__store.setState(s => {
311
+ return {
312
+ ...s,
313
+ matchesById: this.#mergeMatches(s.matchesById, matches)
314
+ };
315
+ });
316
+ await this.loadMatches(matches.map(d => d.id), {
317
+ preload: true,
318
+ maxAge: navigateOpts.maxAge
319
+ });
320
+ return [utils.last(matches), matches];
321
+ };
322
+ cleanMatches = () => {
323
+ const now = Date.now();
324
+ const outdatedMatchIds = Object.values(this.state.matchesById).filter(match => {
325
+ const route = this.getRoute(match.routeId);
326
+ return !this.state.matchIds.includes(match.id) && !this.state.pendingMatchIds.includes(match.id) && (match.preloadMaxAge > -1 ? match.updatedAt + match.preloadMaxAge < now : true) && (route.options.gcMaxAge ? match.updatedAt + route.options.gcMaxAge < now : true);
327
+ }).map(d => d.id);
328
+ if (outdatedMatchIds.length) {
329
+ this.__store.setState(s => {
330
+ const matchesById = {
331
+ ...s.matchesById
332
+ };
333
+ outdatedMatchIds.forEach(id => {
334
+ delete matchesById[id];
335
+ });
336
+ return {
337
+ ...s,
338
+ matchesById
339
+ };
340
+ });
341
+ }
342
+ };
343
+ matchRoutes = (pathname, locationSearch, opts) => {
344
+ let routeParams = {};
345
+ let foundRoute = this.flatRoutes.find(route => {
346
+ const matchedParams = path.matchPathname(this.basepath, path.trimPathRight(pathname), {
347
+ to: route.fullPath,
348
+ caseSensitive: route.options.caseSensitive ?? this.options.caseSensitive
349
+ });
350
+ if (matchedParams) {
351
+ routeParams = matchedParams;
352
+ return true;
353
+ }
354
+ return false;
355
+ });
356
+ let routeCursor = foundRoute || this.routesById['__root__'];
357
+ let matchedRoutes = [routeCursor];
358
+ // let includingLayouts = true
359
+ while (routeCursor?.parentRoute) {
360
+ routeCursor = routeCursor.parentRoute;
361
+ if (routeCursor) matchedRoutes.unshift(routeCursor);
362
+ }
363
+
364
+ // Existing matches are matches that are already loaded along with
365
+ // pending matches that are still loading
366
+
367
+ const parseErrors = matchedRoutes.map(route => {
368
+ let parsedParamsError;
369
+ if (route.options.parseParams) {
370
+ try {
371
+ const parsedParams = route.options.parseParams(routeParams);
372
+ // Add the parsed params to the accumulated params bag
373
+ Object.assign(routeParams, parsedParams);
374
+ } catch (err) {
375
+ parsedParamsError = new PathParamError(err.message, {
376
+ cause: err
377
+ });
378
+ if (opts?.throwOnError) {
379
+ throw parsedParamsError;
380
+ }
381
+ return parsedParamsError;
382
+ }
383
+ }
384
+ return;
385
+ });
386
+ const matches = matchedRoutes.map((route, index) => {
387
+ const interpolatedPath = path.interpolatePath(route.path, routeParams);
388
+ const key = route.options.key ? route.options.key({
389
+ params: routeParams,
390
+ search: locationSearch
391
+ }) ?? '' : '';
392
+ const stringifiedKey = key ? JSON.stringify(key) : '';
393
+ const matchId = path.interpolatePath(route.id, routeParams, true) + stringifiedKey;
394
+
395
+ // Waste not, want not. If we already have a match for this route,
396
+ // reuse it. This is important for layout routes, which might stick
397
+ // around between navigation actions that only change leaf routes.
398
+ const existingMatch = this.getRouteMatch(matchId);
399
+ if (existingMatch) {
400
+ return {
401
+ ...existingMatch
402
+ };
403
+ }
404
+
405
+ // Create a fresh route match
406
+ const hasLoaders = !!(route.options.loader || componentTypes.some(d => route.options[d]?.preload));
407
+ const routeMatch = {
408
+ id: matchId,
409
+ key: stringifiedKey,
410
+ routeId: route.id,
411
+ params: routeParams,
412
+ pathname: path.joinPaths([this.basepath, interpolatedPath]),
413
+ updatedAt: Date.now(),
414
+ maxAge: -1,
415
+ preloadMaxAge: -1,
416
+ routeSearch: {},
417
+ search: {},
418
+ status: hasLoaders ? 'pending' : 'success',
419
+ isFetching: false,
420
+ invalid: false,
421
+ error: undefined,
422
+ paramsError: parseErrors[index],
423
+ searchError: undefined,
424
+ loaderData: undefined,
425
+ loadPromise: Promise.resolve(),
426
+ routeContext: undefined,
427
+ context: undefined,
428
+ abortController: new AbortController(),
429
+ fetchedAt: 0
430
+ };
431
+ return routeMatch;
432
+ });
433
+
434
+ // Take each match and resolve its search params and context
435
+ // This has to happen after the matches are created or found
436
+ // so that we can use the parent match's search params and context
437
+ matches.forEach((match, i) => {
438
+ const parentMatch = matches[i - 1];
439
+ const route = this.getRoute(match.routeId);
440
+ const searchInfo = (() => {
441
+ // Validate the search params and stabilize them
442
+ const parentSearchInfo = {
443
+ search: parentMatch?.search ?? locationSearch,
444
+ routeSearch: parentMatch?.routeSearch ?? locationSearch
445
+ };
446
+ try {
447
+ const validator = typeof route.options.validateSearch === 'object' ? route.options.validateSearch.parse : route.options.validateSearch;
448
+ let routeSearch = validator?.(parentSearchInfo.search) ?? {};
449
+ let search = {
450
+ ...parentSearchInfo.search,
451
+ ...routeSearch
452
+ };
453
+ routeSearch = utils.replaceEqualDeep(match.routeSearch, routeSearch);
454
+ search = utils.replaceEqualDeep(match.search, search);
455
+ return {
456
+ routeSearch,
457
+ search,
458
+ searchDidChange: match.routeSearch !== routeSearch
459
+ };
460
+ } catch (err) {
461
+ match.searchError = new SearchParamError(err.message, {
462
+ cause: err
463
+ });
464
+ if (opts?.throwOnError) {
465
+ throw match.searchError;
466
+ }
467
+ return parentSearchInfo;
468
+ }
469
+ })();
470
+ Object.assign(match, searchInfo);
471
+ });
472
+ return matches;
473
+ };
474
+ loadMatches = async (matchIds, opts) => {
475
+ const getFreshMatches = () => matchIds.map(d => this.getRouteMatch(d));
476
+ if (!opts?.preload) {
477
+ getFreshMatches().forEach(match => {
478
+ // Update each match with its latest route data
479
+ this.setRouteMatch(match.id, s => ({
480
+ ...s,
481
+ routeSearch: match.routeSearch,
482
+ search: match.search,
483
+ routeContext: match.routeContext,
484
+ context: match.context,
485
+ error: match.error,
486
+ paramsError: match.paramsError,
487
+ searchError: match.searchError,
488
+ params: match.params,
489
+ preloadMaxAge: 0
490
+ }));
491
+ });
492
+ }
493
+ let firstBadMatchIndex;
494
+
495
+ // Check each match middleware to see if the route can be accessed
496
+ try {
497
+ for (const [index, match] of getFreshMatches().entries()) {
498
+ const parentMatch = getFreshMatches()[index - 1];
499
+ const route = this.getRoute(match.routeId);
500
+ const handleError = (err, code) => {
501
+ err.routerCode = code;
502
+ firstBadMatchIndex = firstBadMatchIndex ?? index;
503
+ if (isRedirect(err)) {
504
+ throw err;
505
+ }
506
+ try {
507
+ route.options.onError?.(err);
508
+ } catch (errorHandlerErr) {
509
+ err = errorHandlerErr;
510
+ if (isRedirect(errorHandlerErr)) {
511
+ throw errorHandlerErr;
512
+ }
513
+ }
514
+ this.setRouteMatch(match.id, s => ({
515
+ ...s,
516
+ error: err,
517
+ status: 'error',
518
+ updatedAt: Date.now()
519
+ }));
520
+ };
521
+ if (match.paramsError) {
522
+ handleError(match.paramsError, 'PARSE_PARAMS');
523
+ }
524
+ if (match.searchError) {
525
+ handleError(match.searchError, 'VALIDATE_SEARCH');
526
+ }
527
+ let didError = false;
528
+ try {
529
+ const routeContext = (await route.options.beforeLoad?.({
530
+ ...match,
531
+ preload: !!opts?.preload,
532
+ parentContext: parentMatch?.routeContext ?? {},
533
+ context: parentMatch?.context ?? this?.options.context ?? {}
534
+ })) ?? {};
535
+ const context = {
536
+ ...(parentMatch?.context ?? this?.options.context),
537
+ ...routeContext
538
+ };
539
+ this.setRouteMatch(match.id, s => ({
540
+ ...s,
541
+ context,
542
+ routeContext
543
+ }));
544
+ } catch (err) {
545
+ handleError(err, 'BEFORE_LOAD');
546
+ didError = true;
547
+ }
548
+
549
+ // If we errored, do not run the next matches' middleware
550
+ if (didError) {
551
+ break;
552
+ }
553
+ }
554
+ } catch (err) {
555
+ if (isRedirect(err)) {
556
+ if (!opts?.preload) this.navigate(err);
557
+ return;
558
+ }
559
+ throw err;
560
+ }
561
+ const validResolvedMatches = getFreshMatches().slice(0, firstBadMatchIndex);
562
+ const matchPromises = [];
563
+ validResolvedMatches.forEach((match, index) => {
564
+ matchPromises.push((async () => {
565
+ const parentMatchPromise = matchPromises[index - 1];
566
+ const route = this.getRoute(match.routeId);
567
+ if (match.isFetching || match.status === 'success' && !isMatchInvalid(match, {
568
+ preload: opts?.preload
569
+ })) {
570
+ return this.getRouteMatch(match.id)?.loadPromise;
571
+ }
572
+ const fetchedAt = Date.now();
573
+ const checkLatest = () => {
574
+ const latest = this.getRouteMatch(match.id);
575
+ return latest && latest.fetchedAt !== fetchedAt ? latest.loadPromise : undefined;
576
+ };
577
+ const handleIfRedirect = err => {
578
+ if (isRedirect(err)) {
579
+ if (!opts?.preload) {
580
+ this.navigate(err);
581
+ }
582
+ return true;
583
+ }
584
+ return false;
585
+ };
586
+ const load = async () => {
587
+ let latestPromise;
588
+ try {
589
+ const componentsPromise = Promise.all(componentTypes.map(async type => {
590
+ const component = route.options[type];
591
+ if (component?.preload) {
592
+ await component.preload();
593
+ }
594
+ }));
595
+ const loaderPromise = route.options.loader?.({
596
+ ...match,
597
+ preload: !!opts?.preload,
598
+ parentMatchPromise
599
+ });
600
+ const [_, loader] = await Promise.all([componentsPromise, loaderPromise]);
601
+ if (latestPromise = checkLatest()) return await latestPromise;
602
+ this.setRouteMatchData(match.id, () => loader, opts);
603
+ } catch (error) {
604
+ if (latestPromise = checkLatest()) return await latestPromise;
605
+ if (handleIfRedirect(error)) return;
606
+ try {
607
+ route.options.onError?.(error);
608
+ } catch (onErrorError) {
609
+ error = onErrorError;
610
+ if (handleIfRedirect(onErrorError)) return;
611
+ }
612
+ this.setRouteMatch(match.id, s => ({
613
+ ...s,
614
+ error,
615
+ status: 'error',
616
+ isFetching: false,
617
+ updatedAt: Date.now()
618
+ }));
619
+ }
620
+ };
621
+ let loadPromise;
622
+ this.__store.batch(() => {
623
+ this.setRouteMatch(match.id, s => ({
624
+ ...s,
625
+ // status: s.status !== 'success' ? 'pending' : s.status,
626
+ isFetching: true,
627
+ fetchedAt,
628
+ invalid: false
629
+ }));
630
+ loadPromise = load();
631
+ this.setRouteMatch(match.id, s => ({
632
+ ...s,
633
+ loadPromise
634
+ }));
635
+ });
636
+ await loadPromise;
637
+ })());
638
+ });
639
+ await Promise.all(matchPromises);
640
+ };
641
+ resolvePath = (from, path$1) => {
642
+ return path.resolvePath(this.basepath, from, path.cleanPath(path$1));
643
+ };
644
+ navigate = async ({
645
+ from,
646
+ to = '',
647
+ ...rest
648
+ }) => {
649
+ // If this link simply reloads the current route,
650
+ // make sure it has a new key so it will trigger a data refresh
651
+
652
+ // If this `to` is a valid external URL, return
653
+ // null for LinkUtils
654
+ const toString = String(to);
655
+ const fromString = typeof from === 'undefined' ? from : String(from);
656
+ let isExternal;
657
+ try {
658
+ new URL(`${toString}`);
659
+ isExternal = true;
660
+ } catch (e) {}
661
+ invariant__default["default"](!isExternal, 'Attempting to navigate to external url with this.navigate!');
662
+ return this.#buildAndCommitLocation({
663
+ ...rest,
664
+ from: fromString,
665
+ to: toString
666
+ });
667
+ };
668
+ matchRoute = (location, opts) => {
669
+ location = {
670
+ ...location,
671
+ to: location.to ? this.resolvePath(location.from ?? '', location.to) : undefined
672
+ };
673
+ const next = this.buildLocation(location);
674
+ if (opts?.pending && this.state.status !== 'pending') {
675
+ return false;
676
+ }
677
+ const baseLocation = opts?.pending ? this.state.location : this.state.resolvedLocation;
678
+ if (!baseLocation) {
679
+ return false;
680
+ }
681
+ const match = path.matchPathname(this.basepath, baseLocation.pathname, {
682
+ ...opts,
683
+ to: next.pathname
684
+ });
685
+ if (!match) {
686
+ return false;
687
+ }
688
+ if (opts?.includeSearch ?? true) {
689
+ return utils.partialDeepEqual(baseLocation.search, next.search) ? match : false;
690
+ }
691
+ return match;
692
+ };
693
+ buildLink = dest => {
694
+ // If this link simply reloads the current route,
695
+ // make sure it has a new key so it will trigger a data refresh
696
+
697
+ // If this `to` is a valid external URL, return
698
+ // null for LinkUtils
699
+
700
+ const {
701
+ to,
702
+ preload: userPreload,
703
+ preloadDelay: userPreloadDelay,
704
+ activeOptions,
705
+ disabled,
706
+ target,
707
+ replace,
708
+ resetScroll
709
+ } = dest;
710
+ try {
711
+ new URL(`${to}`);
712
+ return {
713
+ type: 'external',
714
+ href: to
715
+ };
716
+ } catch (e) {}
717
+ const nextOpts = dest;
718
+ const next = this.buildLocation(nextOpts);
719
+ const preload = userPreload ?? this.options.defaultPreload;
720
+ const preloadDelay = userPreloadDelay ?? this.options.defaultPreloadDelay ?? 0;
721
+
722
+ // Compare path/hash for matches
723
+ const currentPathSplit = this.state.location.pathname.split('/');
724
+ const nextPathSplit = next.pathname.split('/');
725
+ const pathIsFuzzyEqual = nextPathSplit.every((d, i) => d === currentPathSplit[i]);
726
+ // Combine the matches based on user options
727
+ const pathTest = activeOptions?.exact ? this.state.location.pathname === next.pathname : pathIsFuzzyEqual;
728
+ const hashTest = activeOptions?.includeHash ? this.state.location.hash === next.hash : true;
729
+ const searchTest = activeOptions?.includeSearch ?? true ? utils.partialDeepEqual(this.state.location.search, next.search) : true;
730
+
731
+ // The final "active" test
732
+ const isActive = pathTest && hashTest && searchTest;
733
+
734
+ // The click handler
735
+ const handleClick = e => {
736
+ if (!disabled && !isCtrlEvent(e) && !e.defaultPrevented && (!target || target === '_self') && e.button === 0) {
737
+ e.preventDefault();
738
+
739
+ // All is well? Navigate!
740
+ this.#commitLocation({
741
+ ...next,
742
+ replace,
743
+ resetScroll
744
+ });
745
+ }
746
+ };
747
+
748
+ // The click handler
749
+ const handleFocus = e => {
750
+ if (preload) {
751
+ this.preloadRoute(nextOpts).catch(err => {
752
+ console.warn(err);
753
+ console.warn(preloadWarning);
754
+ });
755
+ }
756
+ };
757
+ const handleTouchStart = e => {
758
+ this.preloadRoute(nextOpts).catch(err => {
759
+ console.warn(err);
760
+ console.warn(preloadWarning);
761
+ });
762
+ };
763
+ const handleEnter = e => {
764
+ const target = e.target || {};
765
+ if (preload) {
766
+ if (target.preloadTimeout) {
767
+ return;
768
+ }
769
+ target.preloadTimeout = setTimeout(() => {
770
+ target.preloadTimeout = null;
771
+ this.preloadRoute(nextOpts).catch(err => {
772
+ console.warn(err);
773
+ console.warn(preloadWarning);
774
+ });
775
+ }, preloadDelay);
776
+ }
777
+ };
778
+ const handleLeave = e => {
779
+ const target = e.target || {};
780
+ if (target.preloadTimeout) {
781
+ clearTimeout(target.preloadTimeout);
782
+ target.preloadTimeout = null;
783
+ }
784
+ };
785
+ return {
786
+ type: 'internal',
787
+ next,
788
+ handleFocus,
789
+ handleClick,
790
+ handleEnter,
791
+ handleLeave,
792
+ handleTouchStart,
793
+ isActive,
794
+ disabled
795
+ };
796
+ };
797
+ dehydrate = () => {
798
+ return {
799
+ state: {
800
+ matchIds: this.state.matchIds,
801
+ dehydratedMatches: this.state.matches.map(d => utils.pick(d, ['fetchedAt', 'invalid', 'preloadMaxAge', 'maxAge', 'id', 'loaderData', 'status', 'updatedAt']))
802
+ }
803
+ };
804
+ };
805
+ hydrate = async __do_not_use_server_ctx => {
806
+ let _ctx = __do_not_use_server_ctx;
807
+ // Client hydrates from window
808
+ if (typeof document !== 'undefined') {
809
+ _ctx = window.__TSR_DEHYDRATED__;
810
+ }
811
+ invariant__default["default"](_ctx, 'Expected to find a __TSR_DEHYDRATED__ property on window... but we did not. Did you forget to render <DehydrateRouter /> in your app?');
812
+ const ctx = _ctx;
813
+ this.dehydratedData = ctx.payload;
814
+ this.options.hydrate?.(ctx.payload);
815
+ const dehydratedState = ctx.router.state;
816
+ let matches = this.matchRoutes(this.state.location.pathname, this.state.location.search).map(match => {
817
+ const dehydratedMatch = dehydratedState.dehydratedMatches.find(d => d.id === match.id);
818
+ invariant__default["default"](dehydratedMatch, `Could not find a client-side match for dehydrated match with id: ${match.id}!`);
819
+ if (dehydratedMatch) {
820
+ return {
821
+ ...match,
822
+ ...dehydratedMatch
823
+ };
824
+ }
825
+ return match;
826
+ });
827
+ this.__store.setState(s => {
828
+ return {
829
+ ...s,
830
+ matchIds: dehydratedState.matchIds,
831
+ matches,
832
+ matchesById: this.#mergeMatches(s.matchesById, matches)
833
+ };
834
+ });
835
+ };
836
+ injectedHtml = [];
837
+ injectHtml = async html => {
838
+ this.injectedHtml.push(html);
839
+ };
840
+ dehydrateData = (key, getData) => {
841
+ if (typeof document === 'undefined') {
842
+ const strKey = typeof key === 'string' ? key : JSON.stringify(key);
843
+ this.injectHtml(async () => {
844
+ const id = `__TSR_DEHYDRATED__${strKey}`;
845
+ const data = typeof getData === 'function' ? await getData() : getData;
846
+ return `<script id='${id}' suppressHydrationWarning>window["__TSR_DEHYDRATED__${escapeJSON(strKey)}"] = ${JSON.stringify(data)}
847
+ ;(() => {
848
+ var el = document.getElementById('${id}')
849
+ el.parentElement.removeChild(el)
850
+ })()
851
+ </script>`;
852
+ });
853
+ return () => this.hydrateData(key);
854
+ }
855
+ return () => undefined;
856
+ };
857
+ hydrateData = key => {
858
+ if (typeof document !== 'undefined') {
859
+ const strKey = typeof key === 'string' ? key : JSON.stringify(key);
860
+ return window[`__TSR_DEHYDRATED__${strKey}`];
861
+ }
862
+ return undefined;
863
+ };
864
+
865
+ // resolveMatchPromise = (matchId: string, key: string, value: any) => {
866
+ // this.state.matches
867
+ // .find((d) => d.id === matchId)
868
+ // ?.__promisesByKey[key]?.resolve(value)
869
+ // }
870
+
871
+ #processRoutes = routeTree => {
872
+ this.routeTree = routeTree;
873
+ this.routesById = {};
874
+ this.routesByPath = {};
875
+ this.flatRoutes = [];
876
+ const recurseRoutes = routes => {
877
+ routes.forEach((route, i) => {
878
+ route.init({
879
+ originalIndex: i,
880
+ router: this
881
+ });
882
+ const existingRoute = this.routesById[route.id];
883
+ invariant__default["default"](!existingRoute, `Duplicate routes found with id: ${String(route.id)}`);
884
+ this.routesById[route.id] = route;
885
+ if (!route.isRoot && route.path) {
886
+ const trimmedFullPath = path.trimPathRight(route.fullPath);
887
+ if (!this.routesByPath[trimmedFullPath] || route.fullPath.endsWith('/')) {
888
+ this.routesByPath[trimmedFullPath] = route;
889
+ }
890
+ }
891
+ const children = route.children;
892
+ if (children?.length) {
893
+ recurseRoutes(children);
894
+ }
895
+ });
896
+ };
897
+ recurseRoutes([routeTree]);
898
+ this.flatRoutes = Object.values(this.routesByPath).map((d, i) => {
899
+ const trimmed = path.trimPath(d.fullPath);
900
+ const parsed = path.parsePathname(trimmed);
901
+ while (parsed.length > 1 && parsed[0]?.value === '/') {
902
+ parsed.shift();
903
+ }
904
+ const score = parsed.map(d => {
905
+ if (d.type === 'param') {
906
+ return 0.5;
907
+ }
908
+ if (d.type === 'wildcard') {
909
+ return 0.25;
910
+ }
911
+ return 1;
912
+ });
913
+ return {
914
+ child: d,
915
+ trimmed,
916
+ parsed,
917
+ index: i,
918
+ score
919
+ };
920
+ }).sort((a, b) => {
921
+ let isIndex = a.trimmed === '/' ? 1 : b.trimmed === '/' ? -1 : 0;
922
+ if (isIndex !== 0) return isIndex;
923
+ const length = Math.min(a.score.length, b.score.length);
924
+
925
+ // Sort by length of score
926
+ if (a.score.length !== b.score.length) {
927
+ return b.score.length - a.score.length;
928
+ }
929
+
930
+ // Sort by min available score
931
+ for (let i = 0; i < length; i++) {
932
+ if (a.score[i] !== b.score[i]) {
933
+ return b.score[i] - a.score[i];
934
+ }
935
+ }
936
+
937
+ // Sort by min available parsed value
938
+ for (let i = 0; i < length; i++) {
939
+ if (a.parsed[i].value !== b.parsed[i].value) {
940
+ return a.parsed[i].value > b.parsed[i].value ? 1 : -1;
941
+ }
942
+ }
943
+
944
+ // Sort by length of trimmed full path
945
+ if (a.trimmed !== b.trimmed) {
946
+ return a.trimmed > b.trimmed ? 1 : -1;
947
+ }
948
+
949
+ // Sort by original index
950
+ return a.index - b.index;
951
+ }).map((d, i) => {
952
+ d.child.rank = i;
953
+ return d.child;
954
+ });
955
+ };
956
+ #parseLocation = previousLocation => {
957
+ const parse = ({
958
+ pathname,
959
+ search,
960
+ hash,
961
+ state
962
+ }) => {
963
+ const parsedSearch = this.options.parseSearch(search);
964
+ return {
965
+ pathname: pathname,
966
+ searchStr: search,
967
+ search: utils.replaceEqualDeep(previousLocation?.search, parsedSearch),
968
+ hash: hash.split('#').reverse()[0] ?? '',
969
+ href: `${pathname}${search}${hash}`,
970
+ state: utils.replaceEqualDeep(previousLocation?.state, state)
971
+ };
972
+ };
973
+ const location = parse(this.history.location);
974
+ let {
975
+ __tempLocation,
976
+ __tempKey
977
+ } = location.state;
978
+ if (__tempLocation && (!__tempKey || __tempKey === this.tempLocationKey)) {
979
+ // Sync up the location keys
980
+ const parsedTempLocation = parse(__tempLocation);
981
+ parsedTempLocation.state.key = location.state.key;
982
+ delete parsedTempLocation.state.__tempLocation;
983
+ return {
984
+ ...parsedTempLocation,
985
+ maskedLocation: location
986
+ };
987
+ }
988
+ return location;
989
+ };
990
+ buildLocation = (opts = {}) => {
991
+ const build = (dest = {}, matches) => {
992
+ const from = this.state.location;
993
+ const fromPathname = dest.from ?? from.pathname;
994
+ let pathname = path.resolvePath(this.basepath ?? '/', fromPathname, `${dest.to ?? ''}`);
995
+ const fromMatches = this.matchRoutes(fromPathname, from.search);
996
+ const prevParams = {
997
+ ...utils.last(fromMatches)?.params
998
+ };
999
+ let nextParams = (dest.params ?? true) === true ? prevParams : utils.functionalUpdate(dest.params, prevParams);
1000
+ if (nextParams) {
1001
+ matches?.map(d => this.getRoute(d.routeId).options.stringifyParams).filter(Boolean).forEach(fn => {
1002
+ nextParams = {
1003
+ ...nextParams,
1004
+ ...fn(nextParams)
1005
+ };
1006
+ });
1007
+ }
1008
+ pathname = path.interpolatePath(pathname, nextParams ?? {});
1009
+ const preSearchFilters = matches?.map(match => this.getRoute(match.routeId).options.preSearchFilters ?? []).flat().filter(Boolean) ?? [];
1010
+ const postSearchFilters = matches?.map(match => this.getRoute(match.routeId).options.postSearchFilters ?? []).flat().filter(Boolean) ?? [];
1011
+
1012
+ // Pre filters first
1013
+ const preFilteredSearch = preSearchFilters?.length ? preSearchFilters?.reduce((prev, next) => next(prev), from.search) : from.search;
1014
+
1015
+ // Then the link/navigate function
1016
+ const destSearch = dest.search === true ? preFilteredSearch // Preserve resolvedFrom true
1017
+ : dest.search ? utils.functionalUpdate(dest.search, preFilteredSearch) ?? {} // Updater
1018
+ : preSearchFilters?.length ? preFilteredSearch // Preserve resolvedFrom filters
1019
+ : {};
1020
+
1021
+ // Then post filters
1022
+ const postFilteredSearch = postSearchFilters?.length ? postSearchFilters.reduce((prev, next) => next(prev), destSearch) : destSearch;
1023
+ const search = utils.replaceEqualDeep(from.search, postFilteredSearch);
1024
+ const searchStr = this.options.stringifySearch(search);
1025
+ const hash = dest.hash === true ? from.hash : dest.hash ? utils.functionalUpdate(dest.hash, from.hash) : from.hash;
1026
+ const hashStr = hash ? `#${hash}` : '';
1027
+ let nextState = dest.state === true ? from.state : dest.state ? utils.functionalUpdate(dest.state, from.state) : from.state;
1028
+ nextState = utils.replaceEqualDeep(from.state, nextState);
1029
+ return {
1030
+ pathname,
1031
+ search,
1032
+ searchStr,
1033
+ state: nextState,
1034
+ hash,
1035
+ href: this.history.createHref(`${pathname}${searchStr}${hashStr}`),
1036
+ unmaskOnReload: dest.unmaskOnReload
1037
+ };
1038
+ };
1039
+ const buildWithMatches = (dest = {}, maskedDest) => {
1040
+ let next = build(dest);
1041
+ let maskedNext = maskedDest ? build(maskedDest) : undefined;
1042
+ if (!maskedNext) {
1043
+ let params = {};
1044
+ let foundMask = this.options.routeMasks?.find(d => {
1045
+ const match = path.matchPathname(this.basepath, next.pathname, {
1046
+ to: d.from,
1047
+ fuzzy: false
1048
+ });
1049
+ if (match) {
1050
+ params = match;
1051
+ return true;
1052
+ }
1053
+ return false;
1054
+ });
1055
+ if (foundMask) {
1056
+ foundMask = {
1057
+ ...foundMask,
1058
+ from: path.interpolatePath(foundMask.from, params)
1059
+ };
1060
+ maskedDest = foundMask;
1061
+ maskedNext = build(maskedDest);
1062
+ }
1063
+ }
1064
+ const nextMatches = this.matchRoutes(next.pathname, next.search);
1065
+ const maskedMatches = maskedNext ? this.matchRoutes(maskedNext.pathname, maskedNext.search) : undefined;
1066
+ const maskedFinal = maskedNext ? build(maskedDest, maskedMatches) : undefined;
1067
+ const final = build(dest, nextMatches);
1068
+ if (maskedFinal) {
1069
+ final.maskedLocation = maskedFinal;
1070
+ }
1071
+ return final;
1072
+ };
1073
+ if (opts.mask) {
1074
+ return buildWithMatches(opts, {
1075
+ ...utils.pick(opts, ['from']),
1076
+ ...opts.mask
1077
+ });
1078
+ }
1079
+ return buildWithMatches(opts);
1080
+ };
1081
+ #buildAndCommitLocation = ({
1082
+ replace,
1083
+ resetScroll,
1084
+ ...rest
1085
+ } = {}) => {
1086
+ const location = this.buildLocation(rest);
1087
+ return this.#commitLocation({
1088
+ ...location,
1089
+ replace,
1090
+ resetScroll
1091
+ });
1092
+ };
1093
+ #commitLocation = async next => {
1094
+ if (this.navigateTimeout) clearTimeout(this.navigateTimeout);
1095
+ let nextAction = 'replace';
1096
+ if (!next.replace) {
1097
+ nextAction = 'push';
1098
+ }
1099
+ const isSameUrl = this.state.location.href === next.href;
1100
+ if (isSameUrl) {
1101
+ nextAction = 'replace';
1102
+ }
1103
+ let {
1104
+ maskedLocation,
1105
+ ...nextHistory
1106
+ } = next;
1107
+ if (maskedLocation) {
1108
+ nextHistory = {
1109
+ ...maskedLocation,
1110
+ state: {
1111
+ ...maskedLocation.state,
1112
+ __tempKey: undefined,
1113
+ __tempLocation: {
1114
+ ...nextHistory,
1115
+ search: nextHistory.searchStr,
1116
+ state: {
1117
+ ...nextHistory.state,
1118
+ __tempKey: undefined,
1119
+ __tempLocation: undefined,
1120
+ key: undefined
1121
+ }
1122
+ }
1123
+ }
1124
+ };
1125
+ if (nextHistory.unmaskOnReload ?? this.options.unmaskOnReload ?? false) {
1126
+ nextHistory.state.__tempKey = this.tempLocationKey;
1127
+ }
1128
+ }
1129
+ this.history[nextAction === 'push' ? 'push' : 'replace'](nextHistory.href, nextHistory.state);
1130
+ this.resetNextScroll = next.resetScroll ?? true;
1131
+ return this.latestLoadPromise;
1132
+ };
1133
+ getRouteMatch = id => {
1134
+ return this.state.matchesById[id];
1135
+ };
1136
+ setRouteMatch = (id, updater) => {
1137
+ this.__store.setState(prev => {
1138
+ if (!prev.matchesById[id]) {
1139
+ return prev;
1140
+ }
1141
+ return {
1142
+ ...prev,
1143
+ matchesById: {
1144
+ ...prev.matchesById,
1145
+ [id]: updater(prev.matchesById[id])
1146
+ }
1147
+ };
1148
+ });
1149
+ };
1150
+ setRouteMatchData = (id, updater, opts) => {
1151
+ const match = this.getRouteMatch(id);
1152
+ if (!match) return;
1153
+ const route = this.getRoute(match.routeId);
1154
+ const updatedAt = opts?.updatedAt ?? Date.now();
1155
+ const preloadMaxAge = opts?.maxAge ?? route.options.preloadMaxAge ?? this.options.defaultPreloadMaxAge ?? 5000;
1156
+ const maxAge = opts?.maxAge ?? route.options.maxAge ?? this.options.defaultMaxAge ?? -1;
1157
+ this.setRouteMatch(id, s => ({
1158
+ ...s,
1159
+ error: undefined,
1160
+ status: 'success',
1161
+ isFetching: false,
1162
+ updatedAt: updatedAt,
1163
+ loaderData: utils.functionalUpdate(updater, s.loaderData),
1164
+ preloadMaxAge,
1165
+ maxAge
1166
+ }));
1167
+ };
1168
+ invalidate = async opts => {
1169
+ if (opts?.matchId) {
1170
+ this.setRouteMatch(opts.matchId, s => ({
1171
+ ...s,
1172
+ invalid: true
1173
+ }));
1174
+ const matchIndex = this.state.matches.findIndex(d => d.id === opts.matchId);
1175
+ const childMatch = this.state.matches[matchIndex + 1];
1176
+ if (childMatch) {
1177
+ return this.invalidate({
1178
+ matchId: childMatch.id,
1179
+ reload: false,
1180
+ __fromFocus: opts.__fromFocus
1181
+ });
1182
+ }
1183
+ } else {
1184
+ this.__store.batch(() => {
1185
+ Object.values(this.state.matchesById).forEach(match => {
1186
+ const route = this.getRoute(match.routeId);
1187
+ const shouldInvalidate = opts?.__fromFocus ? route.options.reloadOnWindowFocus ?? true : true;
1188
+ if (shouldInvalidate) {
1189
+ this.setRouteMatch(match.id, s => ({
1190
+ ...s,
1191
+ invalid: true
1192
+ }));
1193
+ }
1194
+ });
1195
+ });
1196
+ }
1197
+ if (opts?.reload ?? true) {
1198
+ return this.load();
1199
+ }
1200
+ };
1201
+ }
1202
+
1203
+ // Detect if we're in the DOM
1204
+ const isServer = typeof window === 'undefined' || !window.document.createElement;
1205
+ function getInitialRouterState() {
1206
+ return {
1207
+ status: 'idle',
1208
+ isFetching: false,
1209
+ resolvedLocation: null,
1210
+ location: null,
1211
+ matchesById: {},
1212
+ matchIds: [],
1213
+ pendingMatchIds: [],
1214
+ matches: [],
1215
+ pendingMatches: [],
1216
+ renderedMatchIds: [],
1217
+ renderedMatches: [],
1218
+ lastUpdated: Date.now()
1219
+ };
1220
+ }
1221
+ function isCtrlEvent(e) {
1222
+ return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
1223
+ }
1224
+ function redirect(opts) {
1225
+ opts.isRedirect = true;
1226
+ return opts;
1227
+ }
1228
+ function isRedirect(obj) {
1229
+ return !!obj?.isRedirect;
1230
+ }
1231
+ class SearchParamError extends Error {}
1232
+ class PathParamError extends Error {}
1233
+ function escapeJSON(jsonString) {
1234
+ return jsonString.replace(/\\/g, '\\\\') // Escape backslashes
1235
+ .replace(/'/g, "\\'") // Escape single quotes
1236
+ .replace(/"/g, '\\"'); // Escape double quotes
1237
+ }
1238
+
1239
+ // A function that takes an import() argument which is a function and returns a new function that will
1240
+ // proxy arguments from the caller to the imported function, retaining all type
1241
+ // information along the way
1242
+ function lazyFn(fn, key) {
1243
+ return async (...args) => {
1244
+ const imported = await fn();
1245
+ return imported[key || 'default'](...args);
1246
+ };
1247
+ }
1248
+ function isMatchInvalid(match, opts) {
1249
+ const now = Date.now();
1250
+ if (match.invalid) {
1251
+ return true;
1252
+ }
1253
+ if (opts?.preload) {
1254
+ return match.preloadMaxAge < 0 ? false : match.updatedAt + match.preloadMaxAge < now;
1255
+ }
1256
+ return match.maxAge < 0 ? false : match.updatedAt + match.maxAge < now;
1257
+ }
1258
+
1259
+ exports.PathParamError = PathParamError;
1260
+ exports.Router = Router;
1261
+ exports.SearchParamError = SearchParamError;
1262
+ exports.componentTypes = componentTypes;
1263
+ exports.isMatchInvalid = isMatchInvalid;
1264
+ exports.isRedirect = isRedirect;
1265
+ exports.lazyFn = lazyFn;
1266
+ exports.redirect = redirect;
1267
+ //# sourceMappingURL=router.js.map