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

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
@@ -1,5 +1,5 @@
1
1
  /**
2
- * router-core
2
+ * @tanstack/router-core/src/index.ts
3
3
  *
4
4
  * Copyright (c) TanStack
5
5
  *
@@ -8,934 +8,329 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- function _extends$1() {
12
- _extends$1 = Object.assign ? Object.assign.bind() : function (target) {
13
- for (var i = 1; i < arguments.length; i++) {
14
- var source = arguments[i];
15
-
16
- for (var key in source) {
17
- if (Object.prototype.hasOwnProperty.call(source, key)) {
18
- target[key] = source[key];
19
- }
20
- }
21
- }
22
-
23
- return target;
24
- };
25
- return _extends$1.apply(this, arguments);
26
- }
27
-
28
- /**
29
- * Actions represent the type of change to a location value.
30
- *
31
- * @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#action
32
- */
33
- var Action;
34
-
35
- (function (Action) {
36
- /**
37
- * A POP indicates a change to an arbitrary index in the history stack, such
38
- * as a back or forward navigation. It does not describe the direction of the
39
- * navigation, only that the current index changed.
40
- *
41
- * Note: This is the default action for newly created history objects.
42
- */
43
- Action["Pop"] = "POP";
44
- /**
45
- * A PUSH indicates a new entry being added to the history stack, such as when
46
- * a link is clicked and a new page loads. When this happens, all subsequent
47
- * entries in the stack are lost.
48
- */
49
-
50
- Action["Push"] = "PUSH";
51
- /**
52
- * A REPLACE indicates the entry at the current index in the history stack
53
- * being replaced by a new one.
54
- */
55
-
56
- Action["Replace"] = "REPLACE";
57
- })(Action || (Action = {}));
58
-
59
- var readOnly = process.env.NODE_ENV !== "production" ? function (obj) {
60
- return Object.freeze(obj);
61
- } : function (obj) {
62
- return obj;
11
+ import invariant from 'tiny-invariant';
12
+ export { default as invariant } from 'tiny-invariant';
13
+ export { default as warning } from 'tiny-warning';
14
+ import { Store } from '@tanstack/store';
15
+
16
+ // While the public API was clearly inspired by the "history" npm package,
17
+ // This implementation attempts to be more lightweight by
18
+ // making assumptions about the way TanStack Router works
19
+
20
+ const pushStateEvent = 'pushstate';
21
+ const popStateEvent = 'popstate';
22
+ const beforeUnloadEvent = 'beforeunload';
23
+ const beforeUnloadListener = event => {
24
+ event.preventDefault();
25
+ // @ts-ignore
26
+ return event.returnValue = '';
63
27
  };
64
-
65
- function warning$1(cond, message) {
66
- if (!cond) {
67
- // eslint-disable-next-line no-console
68
- if (typeof console !== 'undefined') console.warn(message);
69
-
70
- try {
71
- // Welcome to debugging history!
72
- //
73
- // This error is thrown as a convenience so you can more easily
74
- // find the source for a warning that appears in the console by
75
- // enabling "pause on exceptions" in your JavaScript debugger.
76
- throw new Error(message); // eslint-disable-next-line no-empty
77
- } catch (e) {}
78
- }
79
- }
80
-
81
- var BeforeUnloadEventType = 'beforeunload';
82
- var HashChangeEventType = 'hashchange';
83
- var PopStateEventType = 'popstate';
84
- /**
85
- * Browser history stores the location in regular URLs. This is the standard for
86
- * most web apps, but it requires some configuration on the server to ensure you
87
- * serve the same app at multiple URLs.
88
- *
89
- * @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#createbrowserhistory
90
- */
91
-
92
- function createBrowserHistory(options) {
93
- if (options === void 0) {
94
- options = {};
95
- }
96
-
97
- var _options = options,
98
- _options$window = _options.window,
99
- window = _options$window === void 0 ? document.defaultView : _options$window;
100
- var globalHistory = window.history;
101
-
102
- function getIndexAndLocation() {
103
- var _window$location = window.location,
104
- pathname = _window$location.pathname,
105
- search = _window$location.search,
106
- hash = _window$location.hash;
107
- var state = globalHistory.state || {};
108
- return [state.idx, readOnly({
109
- pathname: pathname,
110
- search: search,
111
- hash: hash,
112
- state: state.usr || null,
113
- key: state.key || 'default'
114
- })];
115
- }
116
-
117
- var blockedPopTx = null;
118
-
119
- function handlePop() {
120
- if (blockedPopTx) {
121
- blockers.call(blockedPopTx);
122
- blockedPopTx = null;
123
- } else {
124
- var nextAction = Action.Pop;
125
-
126
- var _getIndexAndLocation = getIndexAndLocation(),
127
- nextIndex = _getIndexAndLocation[0],
128
- nextLocation = _getIndexAndLocation[1];
129
-
130
- if (blockers.length) {
131
- if (nextIndex != null) {
132
- var delta = index - nextIndex;
133
-
134
- if (delta) {
135
- // Revert the POP
136
- blockedPopTx = {
137
- action: nextAction,
138
- location: nextLocation,
139
- retry: function retry() {
140
- go(delta * -1);
141
- }
142
- };
143
- go(delta);
144
- }
145
- } else {
146
- // Trying to POP to a location with no index. We did not create
147
- // this location, so we can't effectively block the navigation.
148
- process.env.NODE_ENV !== "production" ? warning$1(false, // TODO: Write up a doc that explains our blocking strategy in
149
- // detail and link to it here so people can understand better what
150
- // is going on and how to avoid it.
151
- "You are trying to block a POP navigation to a location that was not " + "created by the history library. The block will fail silently in " + "production, but in general you should do all navigation with the " + "history library (instead of using window.history.pushState directly) " + "to avoid this situation.") : void 0;
152
- }
153
- } else {
154
- applyTx(nextAction);
155
- }
156
- }
157
- }
158
-
159
- window.addEventListener(PopStateEventType, handlePop);
160
- var action = Action.Pop;
161
-
162
- var _getIndexAndLocation2 = getIndexAndLocation(),
163
- index = _getIndexAndLocation2[0],
164
- location = _getIndexAndLocation2[1];
165
-
166
- var listeners = createEvents();
167
- var blockers = createEvents();
168
-
169
- if (index == null) {
170
- index = 0;
171
- globalHistory.replaceState(_extends$1({}, globalHistory.state, {
172
- idx: index
173
- }), '');
174
- }
175
-
176
- function createHref(to) {
177
- return typeof to === 'string' ? to : createPath(to);
178
- } // state defaults to `null` because `window.history.state` does
179
-
180
-
181
- function getNextLocation(to, state) {
182
- if (state === void 0) {
183
- state = null;
184
- }
185
-
186
- return readOnly(_extends$1({
187
- pathname: location.pathname,
188
- hash: '',
189
- search: ''
190
- }, typeof to === 'string' ? parsePath(to) : to, {
191
- state: state,
192
- key: createKey()
193
- }));
194
- }
195
-
196
- function getHistoryStateAndUrl(nextLocation, index) {
197
- return [{
198
- usr: nextLocation.state,
199
- key: nextLocation.key,
200
- idx: index
201
- }, createHref(nextLocation)];
202
- }
203
-
204
- function allowTx(action, location, retry) {
205
- return !blockers.length || (blockers.call({
206
- action: action,
207
- location: location,
208
- retry: retry
209
- }), false);
210
- }
211
-
212
- function applyTx(nextAction) {
213
- action = nextAction;
214
-
215
- var _getIndexAndLocation3 = getIndexAndLocation();
216
-
217
- index = _getIndexAndLocation3[0];
218
- location = _getIndexAndLocation3[1];
219
- listeners.call({
220
- action: action,
221
- location: location
222
- });
223
- }
224
-
225
- function push(to, state) {
226
- var nextAction = Action.Push;
227
- var nextLocation = getNextLocation(to, state);
228
-
229
- function retry() {
230
- push(to, state);
231
- }
232
-
233
- if (allowTx(nextAction, nextLocation, retry)) {
234
- var _getHistoryStateAndUr = getHistoryStateAndUrl(nextLocation, index + 1),
235
- historyState = _getHistoryStateAndUr[0],
236
- url = _getHistoryStateAndUr[1]; // TODO: Support forced reloading
237
- // try...catch because iOS limits us to 100 pushState calls :/
238
-
239
-
240
- try {
241
- globalHistory.pushState(historyState, '', url);
242
- } catch (error) {
243
- // They are going to lose state here, but there is no real
244
- // way to warn them about it since the page will refresh...
245
- window.location.assign(url);
246
- }
247
-
248
- applyTx(nextAction);
28
+ const stopBlocking = () => {
29
+ removeEventListener(beforeUnloadEvent, beforeUnloadListener, {
30
+ capture: true
31
+ });
32
+ };
33
+ function createHistory(opts) {
34
+ let location = opts.getLocation();
35
+ let unsub = () => {};
36
+ let subscribers = new Set();
37
+ let blockers = [];
38
+ let queue = [];
39
+ const tryFlush = () => {
40
+ if (blockers.length) {
41
+ blockers[0]?.(tryFlush, () => {
42
+ blockers = [];
43
+ stopBlocking();
44
+ });
45
+ return;
249
46
  }
250
- }
251
-
252
- function replace(to, state) {
253
- var nextAction = Action.Replace;
254
- var nextLocation = getNextLocation(to, state);
255
-
256
- function retry() {
257
- replace(to, state);
47
+ while (queue.length) {
48
+ queue.shift()?.();
258
49
  }
259
-
260
- if (allowTx(nextAction, nextLocation, retry)) {
261
- var _getHistoryStateAndUr2 = getHistoryStateAndUrl(nextLocation, index),
262
- historyState = _getHistoryStateAndUr2[0],
263
- url = _getHistoryStateAndUr2[1]; // TODO: Support forced reloading
264
-
265
-
266
- globalHistory.replaceState(historyState, '', url);
267
- applyTx(nextAction);
50
+ if (!opts.subscriber) {
51
+ onUpdate();
268
52
  }
269
- }
270
-
271
- function go(delta) {
272
- globalHistory.go(delta);
273
- }
274
-
275
- var history = {
276
- get action() {
277
- return action;
278
- },
279
-
53
+ };
54
+ const queueTask = task => {
55
+ queue.push(task);
56
+ tryFlush();
57
+ };
58
+ const onUpdate = () => {
59
+ location = opts.getLocation();
60
+ subscribers.forEach(subscriber => subscriber());
61
+ };
62
+ return {
280
63
  get location() {
281
64
  return location;
282
65
  },
283
-
284
- createHref: createHref,
285
- push: push,
286
- replace: replace,
287
- go: go,
288
- back: function back() {
289
- go(-1);
66
+ subscribe: cb => {
67
+ if (subscribers.size === 0) {
68
+ unsub = typeof opts.subscriber === 'function' ? opts.subscriber(onUpdate) : () => {};
69
+ }
70
+ subscribers.add(cb);
71
+ return () => {
72
+ subscribers.delete(cb);
73
+ if (subscribers.size === 0) {
74
+ unsub();
75
+ }
76
+ };
77
+ },
78
+ push: (path, state) => {
79
+ assignKey(state);
80
+ queueTask(() => {
81
+ opts.pushState(path, state);
82
+ });
290
83
  },
291
- forward: function forward() {
292
- go(1);
84
+ replace: (path, state) => {
85
+ assignKey(state);
86
+ queueTask(() => {
87
+ opts.replaceState(path, state);
88
+ });
293
89
  },
294
- listen: function listen(listener) {
295
- return listeners.push(listener);
90
+ go: index => {
91
+ queueTask(() => {
92
+ opts.go(index);
93
+ });
296
94
  },
297
- block: function block(blocker) {
298
- var unblock = blockers.push(blocker);
299
-
95
+ back: () => {
96
+ queueTask(() => {
97
+ opts.back();
98
+ });
99
+ },
100
+ forward: () => {
101
+ queueTask(() => {
102
+ opts.forward();
103
+ });
104
+ },
105
+ createHref: str => opts.createHref(str),
106
+ block: cb => {
107
+ blockers.push(cb);
300
108
  if (blockers.length === 1) {
301
- window.addEventListener(BeforeUnloadEventType, promptBeforeUnload);
109
+ addEventListener(beforeUnloadEvent, beforeUnloadListener, {
110
+ capture: true
111
+ });
302
112
  }
303
-
304
- return function () {
305
- unblock(); // Remove the beforeunload listener so the document may
306
- // still be salvageable in the pagehide event.
307
- // See https://html.spec.whatwg.org/#unloading-documents
308
-
113
+ return () => {
114
+ blockers = blockers.filter(b => b !== cb);
309
115
  if (!blockers.length) {
310
- window.removeEventListener(BeforeUnloadEventType, promptBeforeUnload);
116
+ stopBlocking();
311
117
  }
312
118
  };
313
119
  }
314
120
  };
315
- return history;
316
121
  }
317
- /**
318
- * Hash history stores the location in window.location.hash. This makes it ideal
319
- * for situations where you don't want to send the location to the server for
320
- * some reason, either because you do cannot configure it or the URL space is
321
- * reserved for something else.
322
- *
323
- * @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#createhashhistory
324
- */
325
-
326
- function createHashHistory(options) {
327
- if (options === void 0) {
328
- options = {};
329
- }
330
-
331
- var _options2 = options,
332
- _options2$window = _options2.window,
333
- window = _options2$window === void 0 ? document.defaultView : _options2$window;
334
- var globalHistory = window.history;
335
-
336
- function getIndexAndLocation() {
337
- var _parsePath = parsePath(window.location.hash.substr(1)),
338
- _parsePath$pathname = _parsePath.pathname,
339
- pathname = _parsePath$pathname === void 0 ? '/' : _parsePath$pathname,
340
- _parsePath$search = _parsePath.search,
341
- search = _parsePath$search === void 0 ? '' : _parsePath$search,
342
- _parsePath$hash = _parsePath.hash,
343
- hash = _parsePath$hash === void 0 ? '' : _parsePath$hash;
344
-
345
- var state = globalHistory.state || {};
346
- return [state.idx, readOnly({
347
- pathname: pathname,
348
- search: search,
349
- hash: hash,
350
- state: state.usr || null,
351
- key: state.key || 'default'
352
- })];
353
- }
354
-
355
- var blockedPopTx = null;
356
-
357
- function handlePop() {
358
- if (blockedPopTx) {
359
- blockers.call(blockedPopTx);
360
- blockedPopTx = null;
361
- } else {
362
- var nextAction = Action.Pop;
363
-
364
- var _getIndexAndLocation4 = getIndexAndLocation(),
365
- nextIndex = _getIndexAndLocation4[0],
366
- nextLocation = _getIndexAndLocation4[1];
367
-
368
- if (blockers.length) {
369
- if (nextIndex != null) {
370
- var delta = index - nextIndex;
371
-
372
- if (delta) {
373
- // Revert the POP
374
- blockedPopTx = {
375
- action: nextAction,
376
- location: nextLocation,
377
- retry: function retry() {
378
- go(delta * -1);
379
- }
380
- };
381
- go(delta);
382
- }
383
- } else {
384
- // Trying to POP to a location with no index. We did not create
385
- // this location, so we can't effectively block the navigation.
386
- process.env.NODE_ENV !== "production" ? warning$1(false, // TODO: Write up a doc that explains our blocking strategy in
387
- // detail and link to it here so people can understand better
388
- // what is going on and how to avoid it.
389
- "You are trying to block a POP navigation to a location that was not " + "created by the history library. The block will fail silently in " + "production, but in general you should do all navigation with the " + "history library (instead of using window.history.pushState directly) " + "to avoid this situation.") : void 0;
390
- }
391
- } else {
392
- applyTx(nextAction);
393
- }
394
- }
395
- }
396
-
397
- window.addEventListener(PopStateEventType, handlePop); // popstate does not fire on hashchange in IE 11 and old (trident) Edge
398
- // https://developer.mozilla.org/de/docs/Web/API/Window/popstate_event
399
-
400
- window.addEventListener(HashChangeEventType, function () {
401
- var _getIndexAndLocation5 = getIndexAndLocation(),
402
- nextLocation = _getIndexAndLocation5[1]; // Ignore extraneous hashchange events.
403
-
122
+ function assignKey(state) {
123
+ state.key = createRandomKey();
124
+ // if (state.__actualLocation) {
125
+ // state.__actualLocation.state = {
126
+ // ...state.__actualLocation.state,
127
+ // key,
128
+ // }
129
+ // }
130
+ }
404
131
 
405
- if (createPath(nextLocation) !== createPath(location)) {
406
- handlePop();
407
- }
132
+ function createBrowserHistory(opts) {
133
+ const getHref = opts?.getHref ?? (() => `${window.location.pathname}${window.location.search}${window.location.hash}`);
134
+ const createHref = opts?.createHref ?? (path => path);
135
+ const getLocation = () => parseLocation(getHref(), window.history.state);
136
+ return createHistory({
137
+ getLocation,
138
+ subscriber: onUpdate => {
139
+ window.addEventListener(pushStateEvent, onUpdate);
140
+ window.addEventListener(popStateEvent, onUpdate);
141
+ var pushState = window.history.pushState;
142
+ window.history.pushState = function () {
143
+ let res = pushState.apply(history, arguments);
144
+ onUpdate();
145
+ return res;
146
+ };
147
+ var replaceState = window.history.replaceState;
148
+ window.history.replaceState = function () {
149
+ let res = replaceState.apply(history, arguments);
150
+ onUpdate();
151
+ return res;
152
+ };
153
+ return () => {
154
+ window.history.pushState = pushState;
155
+ window.history.replaceState = replaceState;
156
+ window.removeEventListener(pushStateEvent, onUpdate);
157
+ window.removeEventListener(popStateEvent, onUpdate);
158
+ };
159
+ },
160
+ pushState: (path, state) => {
161
+ window.history.pushState(state, '', createHref(path));
162
+ },
163
+ replaceState: (path, state) => {
164
+ window.history.replaceState(state, '', createHref(path));
165
+ },
166
+ back: () => window.history.back(),
167
+ forward: () => window.history.forward(),
168
+ go: n => window.history.go(n),
169
+ createHref: path => createHref(path)
408
170
  });
409
- var action = Action.Pop;
410
-
411
- var _getIndexAndLocation6 = getIndexAndLocation(),
412
- index = _getIndexAndLocation6[0],
413
- location = _getIndexAndLocation6[1];
414
-
415
- var listeners = createEvents();
416
- var blockers = createEvents();
417
-
418
- if (index == null) {
419
- index = 0;
420
- globalHistory.replaceState(_extends$1({}, globalHistory.state, {
421
- idx: index
422
- }), '');
423
- }
424
-
425
- function getBaseHref() {
426
- var base = document.querySelector('base');
427
- var href = '';
171
+ }
172
+ function createHashHistory() {
173
+ return createBrowserHistory({
174
+ getHref: () => window.location.hash.substring(1),
175
+ createHref: path => `#${path}`
176
+ });
177
+ }
178
+ function createMemoryHistory(opts = {
179
+ initialEntries: ['/']
180
+ }) {
181
+ const entries = opts.initialEntries;
182
+ let index = opts.initialIndex ?? entries.length - 1;
183
+ let currentState = {
184
+ key: createRandomKey()
185
+ };
186
+ const getLocation = () => parseLocation(entries[index], currentState);
187
+ return createHistory({
188
+ getLocation,
189
+ subscriber: false,
190
+ pushState: (path, state) => {
191
+ currentState = state;
192
+ entries.push(path);
193
+ index++;
194
+ },
195
+ replaceState: (path, state) => {
196
+ currentState = state;
197
+ entries[index] = path;
198
+ },
199
+ back: () => {
200
+ index--;
201
+ },
202
+ forward: () => {
203
+ index = Math.min(index + 1, entries.length - 1);
204
+ },
205
+ go: n => window.history.go(n),
206
+ createHref: path => path
207
+ });
208
+ }
209
+ function parseLocation(href, state) {
210
+ let hashIndex = href.indexOf('#');
211
+ let searchIndex = href.indexOf('?');
212
+ return {
213
+ href,
214
+ pathname: href.substring(0, hashIndex > 0 ? searchIndex > 0 ? Math.min(hashIndex, searchIndex) : hashIndex : searchIndex > 0 ? searchIndex : href.length),
215
+ hash: hashIndex > -1 ? href.substring(hashIndex) : '',
216
+ search: searchIndex > -1 ? href.slice(searchIndex, hashIndex === -1 ? undefined : hashIndex) : '',
217
+ state: state || {}
218
+ };
219
+ }
428
220
 
429
- if (base && base.getAttribute('href')) {
430
- var url = window.location.href;
431
- var hashIndex = url.indexOf('#');
432
- href = hashIndex === -1 ? url : url.slice(0, hashIndex);
433
- }
221
+ // Thanks co-pilot!
222
+ function createRandomKey() {
223
+ return (Math.random() + 1).toString(36).substring(7);
224
+ }
434
225
 
435
- return href;
226
+ // type Compute<T> = { [K in keyof T]: T[K] } | never
227
+
228
+ // type AllKeys<T> = T extends any ? keyof T : never
229
+
230
+ // export type MergeUnion<T, Keys extends keyof T = keyof T> = Compute<
231
+ // {
232
+ // [K in Keys]: T[Keys]
233
+ // } & {
234
+ // [K in AllKeys<T>]?: T extends any
235
+ // ? K extends keyof T
236
+ // ? T[K]
237
+ // : never
238
+ // : never
239
+ // }
240
+ // >
241
+ function last(arr) {
242
+ return arr[arr.length - 1];
243
+ }
244
+ function isFunction(d) {
245
+ return typeof d === 'function';
246
+ }
247
+ function functionalUpdate(updater, previous) {
248
+ if (isFunction(updater)) {
249
+ return updater(previous);
436
250
  }
251
+ return updater;
252
+ }
253
+ function pick(parent, keys) {
254
+ return keys.reduce((obj, key) => {
255
+ obj[key] = parent[key];
256
+ return obj;
257
+ }, {});
258
+ }
437
259
 
438
- function createHref(to) {
439
- return getBaseHref() + '#' + (typeof to === 'string' ? to : createPath(to));
260
+ /**
261
+ * This function returns `a` if `b` is deeply equal.
262
+ * If not, it will replace any deeply equal children of `b` with those of `a`.
263
+ * This can be used for structural sharing between immutable JSON values for example.
264
+ * Do not use this with signals
265
+ */
266
+ function replaceEqualDeep(prev, _next) {
267
+ if (prev === _next) {
268
+ return prev;
440
269
  }
441
-
442
- function getNextLocation(to, state) {
443
- if (state === void 0) {
444
- state = null;
270
+ const next = _next;
271
+ const array = Array.isArray(prev) && Array.isArray(next);
272
+ if (array || isPlainObject(prev) && isPlainObject(next)) {
273
+ const prevSize = array ? prev.length : Object.keys(prev).length;
274
+ const nextItems = array ? next : Object.keys(next);
275
+ const nextSize = nextItems.length;
276
+ const copy = array ? [] : {};
277
+ let equalItems = 0;
278
+ for (let i = 0; i < nextSize; i++) {
279
+ const key = array ? i : nextItems[i];
280
+ copy[key] = replaceEqualDeep(prev[key], next[key]);
281
+ if (copy[key] === prev[key]) {
282
+ equalItems++;
283
+ }
445
284
  }
446
-
447
- return readOnly(_extends$1({
448
- pathname: location.pathname,
449
- hash: '',
450
- search: ''
451
- }, typeof to === 'string' ? parsePath(to) : to, {
452
- state: state,
453
- key: createKey()
454
- }));
455
- }
456
-
457
- function getHistoryStateAndUrl(nextLocation, index) {
458
- return [{
459
- usr: nextLocation.state,
460
- key: nextLocation.key,
461
- idx: index
462
- }, createHref(nextLocation)];
463
- }
464
-
465
- function allowTx(action, location, retry) {
466
- return !blockers.length || (blockers.call({
467
- action: action,
468
- location: location,
469
- retry: retry
470
- }), false);
285
+ return prevSize === nextSize && equalItems === prevSize ? prev : copy;
471
286
  }
287
+ return next;
288
+ }
472
289
 
473
- function applyTx(nextAction) {
474
- action = nextAction;
475
-
476
- var _getIndexAndLocation7 = getIndexAndLocation();
477
-
478
- index = _getIndexAndLocation7[0];
479
- location = _getIndexAndLocation7[1];
480
- listeners.call({
481
- action: action,
482
- location: location
483
- });
290
+ // Copied from: https://github.com/jonschlinkert/is-plain-object
291
+ function isPlainObject(o) {
292
+ if (!hasObjectPrototype(o)) {
293
+ return false;
484
294
  }
485
295
 
486
- function push(to, state) {
487
- var nextAction = Action.Push;
488
- var nextLocation = getNextLocation(to, state);
489
-
490
- function retry() {
491
- push(to, state);
492
- }
493
-
494
- process.env.NODE_ENV !== "production" ? warning$1(nextLocation.pathname.charAt(0) === '/', "Relative pathnames are not supported in hash history.push(" + JSON.stringify(to) + ")") : void 0;
495
-
496
- if (allowTx(nextAction, nextLocation, retry)) {
497
- var _getHistoryStateAndUr3 = getHistoryStateAndUrl(nextLocation, index + 1),
498
- historyState = _getHistoryStateAndUr3[0],
499
- url = _getHistoryStateAndUr3[1]; // TODO: Support forced reloading
500
- // try...catch because iOS limits us to 100 pushState calls :/
501
-
502
-
503
- try {
504
- globalHistory.pushState(historyState, '', url);
505
- } catch (error) {
506
- // They are going to lose state here, but there is no real
507
- // way to warn them about it since the page will refresh...
508
- window.location.assign(url);
509
- }
510
-
511
- applyTx(nextAction);
512
- }
296
+ // If has modified constructor
297
+ const ctor = o.constructor;
298
+ if (typeof ctor === 'undefined') {
299
+ return true;
513
300
  }
514
301
 
515
- function replace(to, state) {
516
- var nextAction = Action.Replace;
517
- var nextLocation = getNextLocation(to, state);
518
-
519
- function retry() {
520
- replace(to, state);
521
- }
522
-
523
- process.env.NODE_ENV !== "production" ? warning$1(nextLocation.pathname.charAt(0) === '/', "Relative pathnames are not supported in hash history.replace(" + JSON.stringify(to) + ")") : void 0;
524
-
525
- if (allowTx(nextAction, nextLocation, retry)) {
526
- var _getHistoryStateAndUr4 = getHistoryStateAndUrl(nextLocation, index),
527
- historyState = _getHistoryStateAndUr4[0],
528
- url = _getHistoryStateAndUr4[1]; // TODO: Support forced reloading
529
-
530
-
531
- globalHistory.replaceState(historyState, '', url);
532
- applyTx(nextAction);
533
- }
302
+ // If has modified prototype
303
+ const prot = ctor.prototype;
304
+ if (!hasObjectPrototype(prot)) {
305
+ return false;
534
306
  }
535
307
 
536
- function go(delta) {
537
- globalHistory.go(delta);
308
+ // If constructor does not have an Object-specific method
309
+ if (!prot.hasOwnProperty('isPrototypeOf')) {
310
+ return false;
538
311
  }
539
312
 
540
- var history = {
541
- get action() {
542
- return action;
543
- },
544
-
545
- get location() {
546
- return location;
547
- },
548
-
549
- createHref: createHref,
550
- push: push,
551
- replace: replace,
552
- go: go,
553
- back: function back() {
554
- go(-1);
555
- },
556
- forward: function forward() {
557
- go(1);
558
- },
559
- listen: function listen(listener) {
560
- return listeners.push(listener);
561
- },
562
- block: function block(blocker) {
563
- var unblock = blockers.push(blocker);
564
-
565
- if (blockers.length === 1) {
566
- window.addEventListener(BeforeUnloadEventType, promptBeforeUnload);
567
- }
568
-
569
- return function () {
570
- unblock(); // Remove the beforeunload listener so the document may
571
- // still be salvageable in the pagehide event.
572
- // See https://html.spec.whatwg.org/#unloading-documents
573
-
574
- if (!blockers.length) {
575
- window.removeEventListener(BeforeUnloadEventType, promptBeforeUnload);
576
- }
577
- };
578
- }
579
- };
580
- return history;
313
+ // Most likely a plain Object
314
+ return true;
581
315
  }
582
- /**
583
- * Memory history stores the current location in memory. It is designed for use
584
- * in stateful non-browser environments like tests and React Native.
585
- *
586
- * @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#creatememoryhistory
587
- */
588
-
589
- function createMemoryHistory(options) {
590
- if (options === void 0) {
591
- options = {};
316
+ function hasObjectPrototype(o) {
317
+ return Object.prototype.toString.call(o) === '[object Object]';
318
+ }
319
+ function partialDeepEqual(a, b) {
320
+ if (a === b) {
321
+ return true;
592
322
  }
593
-
594
- var _options3 = options,
595
- _options3$initialEntr = _options3.initialEntries,
596
- initialEntries = _options3$initialEntr === void 0 ? ['/'] : _options3$initialEntr,
597
- initialIndex = _options3.initialIndex;
598
- var entries = initialEntries.map(function (entry) {
599
- var location = readOnly(_extends$1({
600
- pathname: '/',
601
- search: '',
602
- hash: '',
603
- state: null,
604
- key: createKey()
605
- }, typeof entry === 'string' ? parsePath(entry) : entry));
606
- process.env.NODE_ENV !== "production" ? warning$1(location.pathname.charAt(0) === '/', "Relative pathnames are not supported in createMemoryHistory({ initialEntries }) (invalid entry: " + JSON.stringify(entry) + ")") : void 0;
607
- return location;
608
- });
609
- var index = clamp(initialIndex == null ? entries.length - 1 : initialIndex, 0, entries.length - 1);
610
- var action = Action.Pop;
611
- var location = entries[index];
612
- var listeners = createEvents();
613
- var blockers = createEvents();
614
-
615
- function createHref(to) {
616
- return typeof to === 'string' ? to : createPath(to);
323
+ if (typeof a !== typeof b) {
324
+ return false;
617
325
  }
618
-
619
- function getNextLocation(to, state) {
620
- if (state === void 0) {
621
- state = null;
622
- }
623
-
624
- return readOnly(_extends$1({
625
- pathname: location.pathname,
626
- search: '',
627
- hash: ''
628
- }, typeof to === 'string' ? parsePath(to) : to, {
629
- state: state,
630
- key: createKey()
631
- }));
326
+ if (isPlainObject(a) && isPlainObject(b)) {
327
+ return !Object.keys(b).some(key => !partialDeepEqual(a[key], b[key]));
632
328
  }
633
-
634
- function allowTx(action, location, retry) {
635
- return !blockers.length || (blockers.call({
636
- action: action,
637
- location: location,
638
- retry: retry
639
- }), false);
329
+ if (Array.isArray(a) && Array.isArray(b)) {
330
+ return a.length === b.length && a.every((item, index) => partialDeepEqual(item, b[index]));
640
331
  }
641
-
642
- function applyTx(nextAction, nextLocation) {
643
- action = nextAction;
644
- location = nextLocation;
645
- listeners.call({
646
- action: action,
647
- location: location
648
- });
649
- }
650
-
651
- function push(to, state) {
652
- var nextAction = Action.Push;
653
- var nextLocation = getNextLocation(to, state);
654
-
655
- function retry() {
656
- push(to, state);
657
- }
658
-
659
- process.env.NODE_ENV !== "production" ? warning$1(location.pathname.charAt(0) === '/', "Relative pathnames are not supported in memory history.push(" + JSON.stringify(to) + ")") : void 0;
660
-
661
- if (allowTx(nextAction, nextLocation, retry)) {
662
- index += 1;
663
- entries.splice(index, entries.length, nextLocation);
664
- applyTx(nextAction, nextLocation);
665
- }
666
- }
667
-
668
- function replace(to, state) {
669
- var nextAction = Action.Replace;
670
- var nextLocation = getNextLocation(to, state);
671
-
672
- function retry() {
673
- replace(to, state);
674
- }
675
-
676
- process.env.NODE_ENV !== "production" ? warning$1(location.pathname.charAt(0) === '/', "Relative pathnames are not supported in memory history.replace(" + JSON.stringify(to) + ")") : void 0;
677
-
678
- if (allowTx(nextAction, nextLocation, retry)) {
679
- entries[index] = nextLocation;
680
- applyTx(nextAction, nextLocation);
681
- }
682
- }
683
-
684
- function go(delta) {
685
- var nextIndex = clamp(index + delta, 0, entries.length - 1);
686
- var nextAction = Action.Pop;
687
- var nextLocation = entries[nextIndex];
688
-
689
- function retry() {
690
- go(delta);
691
- }
692
-
693
- if (allowTx(nextAction, nextLocation, retry)) {
694
- index = nextIndex;
695
- applyTx(nextAction, nextLocation);
696
- }
697
- }
698
-
699
- var history = {
700
- get index() {
701
- return index;
702
- },
703
-
704
- get action() {
705
- return action;
706
- },
707
-
708
- get location() {
709
- return location;
710
- },
711
-
712
- createHref: createHref,
713
- push: push,
714
- replace: replace,
715
- go: go,
716
- back: function back() {
717
- go(-1);
718
- },
719
- forward: function forward() {
720
- go(1);
721
- },
722
- listen: function listen(listener) {
723
- return listeners.push(listener);
724
- },
725
- block: function block(blocker) {
726
- return blockers.push(blocker);
727
- }
728
- };
729
- return history;
730
- } ////////////////////////////////////////////////////////////////////////////////
731
- // UTILS
732
- ////////////////////////////////////////////////////////////////////////////////
733
-
734
- function clamp(n, lowerBound, upperBound) {
735
- return Math.min(Math.max(n, lowerBound), upperBound);
736
- }
737
-
738
- function promptBeforeUnload(event) {
739
- // Cancel the event.
740
- event.preventDefault(); // Chrome (and legacy IE) requires returnValue to be set.
741
-
742
- event.returnValue = '';
743
- }
744
-
745
- function createEvents() {
746
- var handlers = [];
747
- return {
748
- get length() {
749
- return handlers.length;
750
- },
751
-
752
- push: function push(fn) {
753
- handlers.push(fn);
754
- return function () {
755
- handlers = handlers.filter(function (handler) {
756
- return handler !== fn;
757
- });
758
- };
759
- },
760
- call: function call(arg) {
761
- handlers.forEach(function (fn) {
762
- return fn && fn(arg);
763
- });
764
- }
765
- };
766
- }
767
-
768
- function createKey() {
769
- return Math.random().toString(36).substr(2, 8);
770
- }
771
- /**
772
- * Creates a string URL path from the given pathname, search, and hash components.
773
- *
774
- * @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#createpath
775
- */
776
-
777
-
778
- function createPath(_ref) {
779
- var _ref$pathname = _ref.pathname,
780
- pathname = _ref$pathname === void 0 ? '/' : _ref$pathname,
781
- _ref$search = _ref.search,
782
- search = _ref$search === void 0 ? '' : _ref$search,
783
- _ref$hash = _ref.hash,
784
- hash = _ref$hash === void 0 ? '' : _ref$hash;
785
- if (search && search !== '?') pathname += search.charAt(0) === '?' ? search : '?' + search;
786
- if (hash && hash !== '#') pathname += hash.charAt(0) === '#' ? hash : '#' + hash;
787
- return pathname;
788
- }
789
- /**
790
- * Parses a string URL path into its separate pathname, search, and hash components.
791
- *
792
- * @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#parsepath
793
- */
794
-
795
- function parsePath(path) {
796
- var parsedPath = {};
797
-
798
- if (path) {
799
- var hashIndex = path.indexOf('#');
800
-
801
- if (hashIndex >= 0) {
802
- parsedPath.hash = path.substr(hashIndex);
803
- path = path.substr(0, hashIndex);
804
- }
805
-
806
- var searchIndex = path.indexOf('?');
807
-
808
- if (searchIndex >= 0) {
809
- parsedPath.search = path.substr(searchIndex);
810
- path = path.substr(0, searchIndex);
811
- }
812
-
813
- if (path) {
814
- parsedPath.pathname = path;
815
- }
816
- }
817
-
818
- return parsedPath;
819
- }
820
-
821
- var isProduction = process.env.NODE_ENV === 'production';
822
- var prefix = 'Invariant failed';
823
- function invariant(condition, message) {
824
- if (condition) {
825
- return;
826
- }
827
- if (isProduction) {
828
- throw new Error(prefix);
829
- }
830
- var provided = typeof message === 'function' ? message() : message;
831
- var value = provided ? "".concat(prefix, ": ").concat(provided) : prefix;
832
- throw new Error(value);
833
- }
834
-
835
- // type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
836
- // k: infer I,
837
- // ) => any
838
- // ? I
839
- // : never
840
-
841
- /**
842
- * This function returns `a` if `b` is deeply equal.
843
- * If not, it will replace any deeply equal children of `b` with those of `a`.
844
- * This can be used for structural sharing between JSON values for example.
845
- */
846
- function replaceEqualDeep(prev, next) {
847
- if (prev === next) {
848
- return prev;
849
- }
850
-
851
- const array = Array.isArray(prev) && Array.isArray(next);
852
-
853
- if (array || isPlainObject(prev) && isPlainObject(next)) {
854
- const aSize = array ? prev.length : Object.keys(prev).length;
855
- const bItems = array ? next : Object.keys(next);
856
- const bSize = bItems.length;
857
- const copy = array ? [] : {};
858
- let equalItems = 0;
859
-
860
- for (let i = 0; i < bSize; i++) {
861
- const key = array ? i : bItems[i];
862
- copy[key] = replaceEqualDeep(prev[key], next[key]);
863
-
864
- if (copy[key] === prev[key]) {
865
- equalItems++;
866
- }
867
- }
868
-
869
- return aSize === bSize && equalItems === aSize ? prev : copy;
870
- }
871
-
872
- return next;
873
- } // Copied from: https://github.com/jonschlinkert/is-plain-object
874
-
875
- function isPlainObject(o) {
876
- if (!hasObjectPrototype(o)) {
877
- return false;
878
- } // If has modified constructor
879
-
880
-
881
- const ctor = o.constructor;
882
-
883
- if (typeof ctor === 'undefined') {
884
- return true;
885
- } // If has modified prototype
886
-
887
-
888
- const prot = ctor.prototype;
889
-
890
- if (!hasObjectPrototype(prot)) {
891
- return false;
892
- } // If constructor does not have an Object-specific method
893
-
894
-
895
- if (!prot.hasOwnProperty('isPrototypeOf')) {
896
- return false;
897
- } // Most likely a plain Object
898
-
899
-
900
- return true;
901
- }
902
-
903
- function hasObjectPrototype(o) {
904
- return Object.prototype.toString.call(o) === '[object Object]';
905
- }
906
-
907
- function last(arr) {
908
- return arr[arr.length - 1];
909
- }
910
- function warning(cond, message) {
911
- if (cond) {
912
- if (typeof console !== 'undefined') console.warn(message);
913
-
914
- try {
915
- throw new Error(message);
916
- } catch (_unused) {}
917
- }
918
-
919
- return true;
920
- }
921
-
922
- function isFunction(d) {
923
- return typeof d === 'function';
924
- }
925
-
926
- function functionalUpdate(updater, previous) {
927
- if (isFunction(updater)) {
928
- return updater(previous);
929
- }
930
-
931
- return updater;
932
- }
933
- function pick(parent, keys) {
934
- return keys.reduce((obj, key) => {
935
- obj[key] = parent[key];
936
- return obj;
937
- }, {});
938
- }
332
+ return false;
333
+ }
939
334
 
940
335
  function joinPaths(paths) {
941
336
  return cleanPath(paths.filter(Boolean).join('/'));
@@ -954,8 +349,8 @@ function trimPath(path) {
954
349
  return trimPathRight(trimPathLeft(path));
955
350
  }
956
351
  function resolvePath(basepath, base, to) {
957
- base = base.replace(new RegExp("^" + basepath), '/');
958
- to = to.replace(new RegExp("^" + basepath), '/');
352
+ base = base.replace(new RegExp(`^${basepath}`), '/');
353
+ to = to.replace(new RegExp(`^${basepath}`), '/');
959
354
  let baseSegments = parsePathname(base);
960
355
  const toSegments = parsePathname(to);
961
356
  toSegments.forEach((toSegment, index) => {
@@ -968,13 +363,10 @@ function resolvePath(basepath, base, to) {
968
363
  baseSegments.push(toSegment);
969
364
  } else ;
970
365
  } else if (toSegment.value === '..') {
971
- var _last;
972
-
973
366
  // Extra trailing slash? pop it off
974
- if (baseSegments.length > 1 && ((_last = last(baseSegments)) == null ? void 0 : _last.value) === '/') {
367
+ if (baseSegments.length > 1 && last(baseSegments)?.value === '/') {
975
368
  baseSegments.pop();
976
369
  }
977
-
978
370
  baseSegments.pop();
979
371
  } else if (toSegment.value === '.') {
980
372
  return;
@@ -989,10 +381,8 @@ function parsePathname(pathname) {
989
381
  if (!pathname) {
990
382
  return [];
991
383
  }
992
-
993
384
  pathname = cleanPath(pathname);
994
385
  const segments = [];
995
-
996
386
  if (pathname.slice(0, 1) === '/') {
997
387
  pathname = pathname.substring(1);
998
388
  segments.push({
@@ -1000,34 +390,30 @@ function parsePathname(pathname) {
1000
390
  value: '/'
1001
391
  });
1002
392
  }
1003
-
1004
393
  if (!pathname) {
1005
394
  return segments;
1006
- } // Remove empty segments and '.' segments
1007
-
395
+ }
1008
396
 
397
+ // Remove empty segments and '.' segments
1009
398
  const split = pathname.split('/').filter(Boolean);
1010
399
  segments.push(...split.map(part => {
1011
- if (part.startsWith('*')) {
400
+ if (part === '$' || part === '*') {
1012
401
  return {
1013
402
  type: 'wildcard',
1014
403
  value: part
1015
404
  };
1016
405
  }
1017
-
1018
- if (part.charAt(0) === ':') {
406
+ if (part.charAt(0) === '$') {
1019
407
  return {
1020
408
  type: 'param',
1021
409
  value: part
1022
410
  };
1023
411
  }
1024
-
1025
412
  return {
1026
413
  type: 'pathname',
1027
414
  value: part
1028
415
  };
1029
416
  }));
1030
-
1031
417
  if (pathname.slice(-1) === '/') {
1032
418
  pathname = pathname.substring(1);
1033
419
  segments.push({
@@ -1035,66 +421,70 @@ function parsePathname(pathname) {
1035
421
  value: '/'
1036
422
  });
1037
423
  }
1038
-
1039
424
  return segments;
1040
425
  }
1041
- function interpolatePath(path, params, leaveWildcard) {
426
+ function interpolatePath(path, params, leaveWildcards = false) {
1042
427
  const interpolatedPathSegments = parsePathname(path);
1043
428
  return joinPaths(interpolatedPathSegments.map(segment => {
1044
- if (segment.value === '*' && !leaveWildcard) {
1045
- return '';
429
+ if (segment.type === 'wildcard') {
430
+ const value = params[segment.value];
431
+ if (leaveWildcards) return `${segment.value}${value ?? ''}`;
432
+ return value;
1046
433
  }
1047
-
1048
434
  if (segment.type === 'param') {
1049
- var _segment$value$substr;
1050
-
1051
- return (_segment$value$substr = params[segment.value.substring(1)]) != null ? _segment$value$substr : '';
435
+ return params[segment.value.substring(1)] ?? '';
1052
436
  }
1053
-
1054
437
  return segment.value;
1055
438
  }));
1056
439
  }
1057
- function matchPathname(currentPathname, matchLocation) {
1058
- const pathParams = matchByPath(currentPathname, matchLocation); // const searchMatched = matchBySearch(currentLocation.search, matchLocation)
440
+ function matchPathname(basepath, currentPathname, matchLocation) {
441
+ const pathParams = matchByPath(basepath, currentPathname, matchLocation);
442
+ // const searchMatched = matchBySearch(location.search, matchLocation)
1059
443
 
1060
444
  if (matchLocation.to && !pathParams) {
1061
445
  return;
1062
- } // if (matchLocation.search && !searchMatched) {
1063
- // return
1064
- // }
1065
-
1066
-
1067
- return pathParams != null ? pathParams : {};
446
+ }
447
+ return pathParams ?? {};
1068
448
  }
1069
- function matchByPath(from, matchLocation) {
1070
- var _matchLocation$to;
1071
-
449
+ function matchByPath(basepath, from, matchLocation) {
450
+ // Remove the base path from the pathname
451
+ from = basepath != '/' ? from.substring(basepath.length) : from;
452
+ // Default to to $ (wildcard)
453
+ const to = `${matchLocation.to ?? '$'}`;
454
+ // Parse the from and to
1072
455
  const baseSegments = parsePathname(from);
1073
- const routeSegments = parsePathname("" + ((_matchLocation$to = matchLocation.to) != null ? _matchLocation$to : '*'));
456
+ const routeSegments = parsePathname(to);
457
+ if (!from.startsWith('/')) {
458
+ baseSegments.unshift({
459
+ type: 'pathname',
460
+ value: '/'
461
+ });
462
+ }
463
+ if (!to.startsWith('/')) {
464
+ routeSegments.unshift({
465
+ type: 'pathname',
466
+ value: '/'
467
+ });
468
+ }
1074
469
  const params = {};
1075
-
1076
470
  let isMatch = (() => {
1077
471
  for (let i = 0; i < Math.max(baseSegments.length, routeSegments.length); i++) {
1078
472
  const baseSegment = baseSegments[i];
1079
473
  const routeSegment = routeSegments[i];
1080
- const isLastRouteSegment = i === routeSegments.length - 1;
1081
- const isLastBaseSegment = i === baseSegments.length - 1;
1082
-
474
+ const isLastBaseSegment = i >= baseSegments.length - 1;
475
+ const isLastRouteSegment = i >= routeSegments.length - 1;
1083
476
  if (routeSegment) {
1084
477
  if (routeSegment.type === 'wildcard') {
1085
- if (baseSegment != null && baseSegment.value) {
478
+ if (baseSegment?.value) {
1086
479
  params['*'] = joinPaths(baseSegments.slice(i).map(d => d.value));
1087
480
  return true;
1088
481
  }
1089
-
1090
482
  return false;
1091
483
  }
1092
-
1093
484
  if (routeSegment.type === 'pathname') {
1094
- if (routeSegment.value === '/' && !(baseSegment != null && baseSegment.value)) {
485
+ if (routeSegment.value === '/' && !baseSegment?.value) {
1095
486
  return true;
1096
487
  }
1097
-
1098
488
  if (baseSegment) {
1099
489
  if (matchLocation.caseSensitive) {
1100
490
  if (routeSegment.value !== baseSegment.value) {
@@ -1105,41 +495,36 @@ function matchByPath(from, matchLocation) {
1105
495
  }
1106
496
  }
1107
497
  }
1108
-
1109
498
  if (!baseSegment) {
1110
499
  return false;
1111
500
  }
1112
-
1113
501
  if (routeSegment.type === 'param') {
1114
- if ((baseSegment == null ? void 0 : baseSegment.value) === '/') {
502
+ if (baseSegment?.value === '/') {
1115
503
  return false;
1116
504
  }
1117
-
1118
- if (!baseSegment.value.startsWith(':')) {
505
+ if (baseSegment.value.charAt(0) !== '$') {
1119
506
  params[routeSegment.value.substring(1)] = baseSegment.value;
1120
507
  }
1121
508
  }
1122
509
  }
1123
-
1124
- if (isLastRouteSegment && !isLastBaseSegment) {
510
+ if (!isLastBaseSegment && isLastRouteSegment) {
1125
511
  return !!matchLocation.fuzzy;
1126
512
  }
1127
513
  }
1128
-
1129
514
  return true;
1130
515
  })();
1131
-
1132
516
  return isMatch ? params : undefined;
1133
517
  }
1134
518
 
1135
519
  // @ts-nocheck
520
+
1136
521
  // qss has been slightly modified and inlined here for our use cases (and compression's sake). We've included it as a hard dependency for MIT license attribution.
522
+
1137
523
  function encode(obj, pfx) {
1138
524
  var k,
1139
- i,
1140
- tmp,
1141
- str = '';
1142
-
525
+ i,
526
+ tmp,
527
+ str = '';
1143
528
  for (k in obj) {
1144
529
  if ((tmp = obj[k]) !== void 0) {
1145
530
  if (Array.isArray(tmp)) {
@@ -1153,1328 +538,1568 @@ function encode(obj, pfx) {
1153
538
  }
1154
539
  }
1155
540
  }
1156
-
1157
541
  return (pfx || '') + str;
1158
542
  }
1159
-
1160
543
  function toValue(mix) {
1161
544
  if (!mix) return '';
1162
545
  var str = decodeURIComponent(mix);
1163
546
  if (str === 'false') return false;
1164
547
  if (str === 'true') return true;
1165
- if (str.charAt(0) === '0') return str;
1166
- return +str * 0 === 0 ? +str : str;
548
+ return +str * 0 === 0 && +str + '' === str ? +str : str;
1167
549
  }
1168
-
1169
550
  function decode(str) {
1170
551
  var tmp,
1171
- k,
1172
- out = {},
1173
- arr = str.split('&');
1174
-
552
+ k,
553
+ out = {},
554
+ arr = str.split('&');
1175
555
  while (tmp = arr.shift()) {
1176
556
  tmp = tmp.split('=');
1177
557
  k = tmp.shift();
1178
-
1179
558
  if (out[k] !== void 0) {
1180
559
  out[k] = [].concat(out[k], toValue(tmp.shift()));
1181
560
  } else {
1182
561
  out[k] = toValue(tmp.shift());
1183
562
  }
1184
563
  }
1185
-
1186
564
  return out;
1187
565
  }
1188
566
 
1189
- function _extends() {
1190
- _extends = Object.assign ? Object.assign.bind() : function (target) {
1191
- for (var i = 1; i < arguments.length; i++) {
1192
- var source = arguments[i];
1193
-
1194
- for (var key in source) {
1195
- if (Object.prototype.hasOwnProperty.call(source, key)) {
1196
- target[key] = source[key];
1197
- }
1198
- }
1199
- }
1200
-
1201
- return target;
1202
- };
1203
- return _extends.apply(this, arguments);
1204
- }
1205
-
1206
- function createRoute(routeConfig, options, parent, router) {
1207
- const {
1208
- id,
1209
- routeId,
1210
- path: routePath,
1211
- fullPath
1212
- } = routeConfig;
1213
-
1214
- const action = router.state.actions[id] || (() => {
1215
- router.state.actions[id] = {
1216
- submissions: [],
1217
- submit: async (submission, actionOpts) => {
1218
- var _actionOpts$invalidat;
1219
-
1220
- if (!route) {
1221
- return;
1222
- }
1223
-
1224
- const invalidate = (_actionOpts$invalidat = actionOpts == null ? void 0 : actionOpts.invalidate) != null ? _actionOpts$invalidat : true;
1225
-
1226
- if (!(actionOpts != null && actionOpts.multi)) {
1227
- action.submissions = action.submissions.filter(d => d.isMulti);
1228
- }
1229
-
1230
- const actionState = {
1231
- submittedAt: Date.now(),
1232
- status: 'pending',
1233
- submission,
1234
- isMulti: !!(actionOpts != null && actionOpts.multi)
1235
- };
1236
- action.current = actionState;
1237
- action.latest = actionState;
1238
- action.submissions.push(actionState);
1239
- router.notify();
1240
-
1241
- try {
1242
- const res = await (route.options.action == null ? void 0 : route.options.action(submission));
1243
- actionState.data = res;
567
+ const rootRouteId = '__root__';
1244
568
 
1245
- if (invalidate) {
1246
- router.invalidateRoute({
1247
- to: '.',
1248
- fromCurrent: true
1249
- });
1250
- await router.reload();
1251
- }
569
+ // The parse type here allows a zod schema to be passed directly to the validator
1252
570
 
1253
- actionState.status = 'success';
1254
- return res;
1255
- } catch (err) {
1256
- console.error(err);
1257
- actionState.error = err;
1258
- actionState.status = 'error';
1259
- } finally {
1260
- router.notify();
1261
- }
1262
- }
1263
- };
1264
- return router.state.actions[id];
1265
- })();
571
+ // T extends Record<PropertyKey, infer U>
572
+ // ? {
573
+ // [K in keyof T]: UseLoaderResultPromise<T[K]>
574
+ // }
575
+ // : UseLoaderResultPromise<T>
1266
576
 
1267
- const loader = router.state.loaders[id] || (() => {
1268
- router.state.loaders[id] = {
1269
- pending: [],
1270
- fetch: async loaderContext => {
1271
- if (!route) {
1272
- return;
1273
- }
577
+ // export type UseLoaderResultPromise<T> = T extends Promise<infer U>
578
+ // ? StreamedPromise<U>
579
+ // : T
580
+ class Route {
581
+ // Set up in this.init()
1274
582
 
1275
- const loaderState = {
1276
- loadedAt: Date.now(),
1277
- loaderContext
1278
- };
1279
- loader.current = loaderState;
1280
- loader.latest = loaderState;
1281
- loader.pending.push(loaderState); // router.state = {
1282
- // ...router.state,
1283
- // currentAction: loaderState,
1284
- // latestAction: loaderState,
1285
- // }
583
+ // customId!: TCustomId
1286
584
 
1287
- router.notify();
585
+ // Optional
1288
586
 
1289
- try {
1290
- return await (route.options.loader == null ? void 0 : route.options.loader(loaderContext));
1291
- } finally {
1292
- loader.pending = loader.pending.filter(d => d !== loaderState); // router.removeActionQueue.push({ loader, loaderState })
587
+ constructor(options) {
588
+ this.options = options || {};
589
+ this.isRoot = !options?.getParentRoute;
590
+ Route.__onInit(this);
591
+ }
592
+ init = opts => {
593
+ this.originalIndex = opts.originalIndex;
594
+ this.router = opts.router;
595
+ const options = this.options;
596
+ const isRoot = !options?.path && !options?.id;
597
+ this.parentRoute = this.options?.getParentRoute?.();
598
+ if (isRoot) {
599
+ this.path = rootRouteId;
600
+ } else {
601
+ invariant(this.parentRoute, `Child Route instances must pass a 'getParentRoute: () => ParentRoute' option that returns a Route instance.`);
602
+ }
603
+ let path = isRoot ? rootRouteId : options.path;
1293
604
 
1294
- router.notify();
1295
- }
1296
- }
1297
- };
1298
- return router.state.loaders[id];
1299
- })();
605
+ // If the path is anything other than an index path, trim it up
606
+ if (path && path !== '/') {
607
+ path = trimPath(path);
608
+ }
609
+ const customId = options?.id || path;
1300
610
 
1301
- let route = {
1302
- routeId: id,
1303
- routeRouteId: routeId,
1304
- routePath,
1305
- fullPath,
1306
- options,
1307
- router,
1308
- childRoutes: undefined,
1309
- parentRoute: parent,
1310
- action,
1311
- loader: loader,
1312
- buildLink: options => {
1313
- return router.buildLink(_extends({}, options, {
1314
- from: fullPath
1315
- }));
1316
- },
1317
- navigate: options => {
1318
- return router.navigate(_extends({}, options, {
1319
- from: fullPath
1320
- }));
1321
- },
1322
- matchRoute: (matchLocation, opts) => {
1323
- return router.matchRoute(_extends({}, matchLocation, {
1324
- from: fullPath
1325
- }), opts);
611
+ // Strip the parentId prefix from the first level of children
612
+ let id = isRoot ? rootRouteId : joinPaths([this.parentRoute.id === rootRouteId ? '' : this.parentRoute.id, customId]);
613
+ if (path === rootRouteId) {
614
+ path = '/';
1326
615
  }
616
+ if (id !== rootRouteId) {
617
+ id = joinPaths(['/', id]);
618
+ }
619
+ const fullPath = id === rootRouteId ? '/' : joinPaths([this.parentRoute.fullPath, path]);
620
+ this.path = path;
621
+ this.id = id;
622
+ // this.customId = customId as TCustomId
623
+ this.fullPath = fullPath;
624
+ this.to = fullPath;
625
+ };
626
+ addChildren = children => {
627
+ this.children = children;
628
+ return this;
629
+ };
630
+ update = options => {
631
+ Object.assign(this.options, options);
632
+ return this;
633
+ };
634
+ static __onInit = route => {
635
+ // This is a dummy static method that should get
636
+ // replaced by a framework specific implementation if necessary
1327
637
  };
1328
- router.options.createRoute == null ? void 0 : router.options.createRoute({
1329
- router,
1330
- route
1331
- });
1332
- return route;
1333
638
  }
1334
-
1335
- const rootRouteId = '__root__';
1336
- const createRouteConfig = function createRouteConfig(options, children, isRoot, parentId, parentPath) {
1337
- if (options === void 0) {
1338
- options = {};
1339
- }
1340
-
1341
- if (isRoot === void 0) {
1342
- isRoot = true;
1343
- }
1344
-
1345
- if (isRoot) {
1346
- options.path = rootRouteId;
1347
- } // Strip the root from parentIds
1348
-
1349
-
1350
- if (parentId === rootRouteId) {
1351
- parentId = '';
1352
- }
1353
-
1354
- let path = isRoot ? rootRouteId : options.path; // If the path is anything other than an index path, trim it up
1355
-
1356
- if (path && path !== '/') {
1357
- path = trimPath(path);
1358
- }
1359
-
1360
- const routeId = path || options.id;
1361
- let id = joinPaths([parentId, routeId]);
1362
-
1363
- if (path === rootRouteId) {
1364
- path = '/';
639
+ class RouterContext {
640
+ constructor() {}
641
+ createRootRoute = options => {
642
+ return new RootRoute(options);
643
+ };
644
+ }
645
+ class RootRoute extends Route {
646
+ constructor(options) {
647
+ super(options);
1365
648
  }
649
+ }
650
+ function createRouteMask(opts) {
651
+ return opts;
652
+ }
1366
653
 
1367
- if (id !== rootRouteId) {
1368
- id = joinPaths(['/', id]);
654
+ class FileRoute {
655
+ constructor(path) {
656
+ this.path = path;
1369
657
  }
1370
-
1371
- const fullPath = id === rootRouteId ? '/' : trimPathRight(joinPaths([parentPath, path]));
1372
- return {
1373
- id: id,
1374
- routeId: routeId,
1375
- path: path,
1376
- fullPath: fullPath,
1377
- options: options,
1378
- children,
1379
- createChildren: cb => createRouteConfig(options, cb(childOptions => createRouteConfig(childOptions, undefined, false, id, fullPath)), false, parentId, parentPath),
1380
- addChildren: children => createRouteConfig(options, children, false, parentId, parentPath),
1381
- createRoute: childOptions => createRouteConfig(childOptions, undefined, false, id, fullPath)
658
+ createRoute = options => {
659
+ const route = new Route(options);
660
+ route.isRoot = false;
661
+ return route;
1382
662
  };
1383
- };
1384
-
1385
- const componentTypes = ['component', 'errorComponent', 'pendingComponent'];
1386
- function createRouteMatch(router, route, opts) {
1387
- const routeMatch = _extends({}, route, opts, {
1388
- router,
1389
- routeSearch: {},
1390
- search: {},
1391
- childMatches: [],
1392
- status: 'idle',
1393
- routeLoaderData: {},
1394
- loaderData: {},
1395
- isFetching: false,
1396
- isInvalid: false,
1397
- invalidAt: Infinity,
1398
- // pendingActions: [],
1399
- getIsInvalid: () => {
1400
- const now = Date.now();
1401
- return routeMatch.isInvalid || routeMatch.invalidAt < now;
1402
- },
1403
- __: {
1404
- abortController: new AbortController(),
1405
- latestId: '',
1406
- resolve: () => {},
1407
- notify: () => {
1408
- routeMatch.__.resolve();
1409
-
1410
- routeMatch.router.notify();
1411
- },
1412
- validate: () => {
1413
- var _routeMatch$parentMat, _routeMatch$parentMat2;
1414
-
1415
- // Validate the search params and stabilize them
1416
- const parentSearch = (_routeMatch$parentMat = (_routeMatch$parentMat2 = routeMatch.parentMatch) == null ? void 0 : _routeMatch$parentMat2.search) != null ? _routeMatch$parentMat : router.location.search;
1417
-
1418
- try {
1419
- var _validator;
1420
-
1421
- const prevSearch = routeMatch.routeSearch;
1422
- const validator = typeof routeMatch.options.validateSearch === 'object' ? routeMatch.options.validateSearch.parse : routeMatch.options.validateSearch;
1423
- let nextSearch = replaceEqualDeep(prevSearch, (_validator = validator == null ? void 0 : validator(parentSearch)) != null ? _validator : {}); // Invalidate route matches when search param stability changes
1424
-
1425
- if (prevSearch !== nextSearch) {
1426
- routeMatch.isInvalid = true;
1427
- }
1428
-
1429
- routeMatch.routeSearch = nextSearch;
1430
- routeMatch.search = replaceEqualDeep(parentSearch, _extends({}, parentSearch, nextSearch));
1431
- componentTypes.map(async type => {
1432
- const component = routeMatch.options[type];
1433
-
1434
- if (typeof routeMatch.__[type] !== 'function') {
1435
- routeMatch.__[type] = component;
1436
- }
1437
- });
1438
- } catch (err) {
1439
- console.error(err);
1440
- const error = new Error('Invalid search params found', {
1441
- cause: err
1442
- });
1443
- error.code = 'INVALID_SEARCH_PARAMS';
1444
- routeMatch.status = 'error';
1445
- routeMatch.error = error; // Do not proceed with loading the route
1446
-
1447
- return;
1448
- }
1449
- }
1450
- },
1451
- cancel: () => {
1452
- var _routeMatch$__$abortC;
1453
-
1454
- (_routeMatch$__$abortC = routeMatch.__.abortController) == null ? void 0 : _routeMatch$__$abortC.abort();
1455
- },
1456
- invalidate: () => {
1457
- routeMatch.isInvalid = true;
1458
- },
1459
- hasLoaders: () => {
1460
- return !!(route.options.loader || componentTypes.some(d => {
1461
- var _route$options$d;
1462
-
1463
- return (_route$options$d = route.options[d]) == null ? void 0 : _route$options$d.preload;
1464
- }));
1465
- },
1466
- load: async loaderOpts => {
1467
- const now = Date.now();
1468
- const minMaxAge = loaderOpts != null && loaderOpts.preload ? Math.max(loaderOpts == null ? void 0 : loaderOpts.maxAge, loaderOpts == null ? void 0 : loaderOpts.gcMaxAge) : 0; // If this is a preload, add it to the preload cache
1469
-
1470
- if (loaderOpts != null && loaderOpts.preload && minMaxAge > 0) {
1471
- // If the match is currently active, don't preload it
1472
- if (router.state.matches.find(d => d.matchId === routeMatch.matchId)) {
1473
- return;
1474
- }
1475
-
1476
- router.matchCache[routeMatch.matchId] = {
1477
- gc: now + loaderOpts.gcMaxAge,
1478
- match: routeMatch
1479
- };
1480
- } // If the match is invalid, errored or idle, trigger it to load
1481
-
1482
-
1483
- if (routeMatch.status === 'success' && routeMatch.getIsInvalid() || routeMatch.status === 'error' || routeMatch.status === 'idle') {
1484
- const maxAge = loaderOpts != null && loaderOpts.preload ? loaderOpts == null ? void 0 : loaderOpts.maxAge : undefined;
1485
- await routeMatch.fetch({
1486
- maxAge
1487
- });
1488
- }
1489
- },
1490
- fetch: async opts => {
1491
- const loadId = '' + Date.now() + Math.random();
1492
- routeMatch.__.latestId = loadId;
1493
-
1494
- const checkLatest = async () => {
1495
- if (loadId !== routeMatch.__.latestId) {
1496
- // warning(true, 'Data loader is out of date!')
1497
- return new Promise(() => {});
1498
- }
1499
- }; // If the match was in an error state, set it
1500
- // to a loading state again. Otherwise, keep it
1501
- // as loading or resolved
1502
-
1503
-
1504
- if (routeMatch.status === 'idle') {
1505
- routeMatch.status = 'loading';
1506
- } // We started loading the route, so it's no longer invalid
1507
-
1508
-
1509
- routeMatch.isInvalid = false;
1510
- routeMatch.__.loadPromise = new Promise(async resolve => {
1511
- // We are now fetching, even if it's in the background of a
1512
- // resolved state
1513
- routeMatch.isFetching = true;
1514
- routeMatch.__.resolve = resolve;
1515
-
1516
- routeMatch.__.componentsPromise = (async () => {
1517
- // then run all component and data loaders in parallel
1518
- // For each component type, potentially load it asynchronously
1519
- await Promise.all(componentTypes.map(async type => {
1520
- var _routeMatch$__$type;
1521
-
1522
- const component = routeMatch.options[type];
1523
-
1524
- if ((_routeMatch$__$type = routeMatch.__[type]) != null && _routeMatch$__$type.preload) {
1525
- routeMatch.__[type] = await router.options.loadComponent(component);
1526
- }
1527
- }));
1528
- })();
1529
-
1530
- routeMatch.__.dataPromise = Promise.resolve().then(async () => {
1531
- try {
1532
- var _ref, _ref2, _opts$maxAge;
1533
-
1534
- if (routeMatch.options.loader) {
1535
- const data = await router.loadMatchData(routeMatch);
1536
- await checkLatest();
1537
- routeMatch.routeLoaderData = replaceEqualDeep(routeMatch.routeLoaderData, data);
1538
- }
1539
-
1540
- routeMatch.error = undefined;
1541
- routeMatch.status = 'success';
1542
- routeMatch.updatedAt = Date.now();
1543
- routeMatch.invalidAt = routeMatch.updatedAt + ((_ref = (_ref2 = (_opts$maxAge = opts == null ? void 0 : opts.maxAge) != null ? _opts$maxAge : routeMatch.options.loaderMaxAge) != null ? _ref2 : router.options.defaultLoaderMaxAge) != null ? _ref : 0);
1544
- return routeMatch.routeLoaderData;
1545
- } catch (err) {
1546
- await checkLatest();
1547
-
1548
- if (process.env.NODE_ENV !== 'production') {
1549
- console.error(err);
1550
- }
1551
-
1552
- routeMatch.error = err;
1553
- routeMatch.status = 'error';
1554
- routeMatch.updatedAt = Date.now();
1555
- throw err;
1556
- }
1557
- });
1558
-
1559
- const after = async () => {
1560
- await checkLatest();
1561
- routeMatch.isFetching = false;
1562
- delete routeMatch.__.loadPromise;
1563
-
1564
- routeMatch.__.notify();
1565
- };
1566
-
1567
- try {
1568
- await Promise.all([routeMatch.__.componentsPromise, routeMatch.__.dataPromise.catch(() => {})]);
1569
- after();
1570
- } catch (_unused) {
1571
- after();
1572
- }
1573
- });
1574
- await routeMatch.__.loadPromise;
1575
- await checkLatest();
1576
- }
1577
- });
1578
-
1579
- if (!routeMatch.hasLoaders()) {
1580
- routeMatch.status = 'success';
1581
- }
1582
-
1583
- return routeMatch;
1584
663
  }
1585
664
 
1586
665
  const defaultParseSearch = parseSearchWith(JSON.parse);
1587
- const defaultStringifySearch = stringifySearchWith(JSON.stringify);
666
+ const defaultStringifySearch = stringifySearchWith(JSON.stringify, JSON.parse);
1588
667
  function parseSearchWith(parser) {
1589
668
  return searchStr => {
1590
669
  if (searchStr.substring(0, 1) === '?') {
1591
670
  searchStr = searchStr.substring(1);
1592
671
  }
672
+ let query = decode(searchStr);
1593
673
 
1594
- let query = decode(searchStr); // Try to parse any query params that might be json
1595
-
674
+ // Try to parse any query params that might be json
1596
675
  for (let key in query) {
1597
676
  const value = query[key];
1598
-
1599
677
  if (typeof value === 'string') {
1600
678
  try {
1601
679
  query[key] = parser(value);
1602
- } catch (err) {//
680
+ } catch (err) {
681
+ //
1603
682
  }
1604
683
  }
1605
684
  }
1606
-
1607
685
  return query;
1608
686
  };
1609
687
  }
1610
- function stringifySearchWith(stringify) {
688
+ function stringifySearchWith(stringify, parser) {
689
+ function stringifyValue(val) {
690
+ if (typeof val === 'object' && val !== null) {
691
+ try {
692
+ return stringify(val);
693
+ } catch (err) {
694
+ // silent
695
+ }
696
+ } else if (typeof val === 'string' && typeof parser === 'function') {
697
+ try {
698
+ // Check if it's a valid parseable string.
699
+ // If it is, then stringify it again.
700
+ parser(val);
701
+ return stringify(val);
702
+ } catch (err) {
703
+ // silent
704
+ }
705
+ }
706
+ return val;
707
+ }
1611
708
  return search => {
1612
- search = _extends({}, search);
1613
-
709
+ search = {
710
+ ...search
711
+ };
1614
712
  if (search) {
1615
713
  Object.keys(search).forEach(key => {
1616
714
  const val = search[key];
1617
-
1618
715
  if (typeof val === 'undefined' || val === undefined) {
1619
716
  delete search[key];
1620
- } else if (val && typeof val === 'object' && val !== null) {
1621
- try {
1622
- search[key] = stringify(val);
1623
- } catch (err) {// silent
1624
- }
717
+ } else {
718
+ search[key] = stringifyValue(val);
1625
719
  }
1626
720
  });
1627
721
  }
1628
-
1629
722
  const searchStr = encode(search).toString();
1630
- return searchStr ? "?" + searchStr : '';
1631
- };
1632
- }
1633
-
1634
- var _window$document;
1635
- // Detect if we're in the DOM
1636
- const isServer = typeof window === 'undefined' || !((_window$document = window.document) != null && _window$document.createElement); // This is the default history object if none is defined
1637
-
1638
- const createDefaultHistory = () => isServer ? createMemoryHistory() : createBrowserHistory();
1639
-
1640
- function getInitialRouterState() {
1641
- return {
1642
- status: 'idle',
1643
- location: null,
1644
- matches: [],
1645
- actions: {},
1646
- loaders: {},
1647
- lastUpdated: Date.now(),
1648
- isFetching: false,
1649
- isPreloading: false
723
+ return searchStr ? `?${searchStr}` : '';
1650
724
  };
1651
725
  }
1652
726
 
1653
- function createRouter(userOptions) {
1654
- var _userOptions$stringif, _userOptions$parseSea;
1655
-
1656
- const history = (userOptions == null ? void 0 : userOptions.history) || createDefaultHistory();
1657
-
1658
- const originalOptions = _extends({
1659
- defaultLoaderGcMaxAge: 5 * 60 * 1000,
1660
- defaultLoaderMaxAge: 0,
1661
- defaultPreloadMaxAge: 2000,
1662
- defaultPreloadDelay: 50
1663
- }, userOptions, {
1664
- stringifySearch: (_userOptions$stringif = userOptions == null ? void 0 : userOptions.stringifySearch) != null ? _userOptions$stringif : defaultStringifySearch,
1665
- parseSearch: (_userOptions$parseSea = userOptions == null ? void 0 : userOptions.parseSearch) != null ? _userOptions$parseSea : defaultParseSearch
1666
- });
727
+ //
1667
728
 
1668
- let router = {
1669
- types: undefined,
1670
- // public api
1671
- history,
1672
- options: originalOptions,
1673
- listeners: [],
1674
- // Resolved after construction
1675
- basepath: '',
1676
- routeTree: undefined,
1677
- routesById: {},
1678
- location: undefined,
1679
- //
1680
- navigationPromise: Promise.resolve(),
1681
- resolveNavigation: () => {},
1682
- matchCache: {},
1683
- state: getInitialRouterState(),
1684
- reset: () => {
1685
- router.state = getInitialRouterState();
1686
- router.notify();
1687
- },
1688
- startedLoadingAt: Date.now(),
1689
- subscribe: listener => {
1690
- router.listeners.push(listener);
1691
- return () => {
1692
- router.listeners = router.listeners.filter(x => x !== listener);
1693
- };
1694
- },
1695
- getRoute: id => {
1696
- return router.routesById[id];
1697
- },
1698
- notify: () => {
1699
- const isFetching = router.state.status === 'loading' || router.state.matches.some(d => d.isFetching);
1700
- const isPreloading = Object.values(router.matchCache).some(d => d.match.isFetching && !router.state.matches.find(dd => dd.matchId === d.match.matchId));
1701
-
1702
- if (router.state.isFetching !== isFetching || router.state.isPreloading !== isPreloading) {
1703
- router.state = _extends({}, router.state, {
1704
- isFetching,
1705
- isPreloading
1706
- });
1707
- }
729
+ const componentTypes = ['component', 'errorComponent', 'pendingComponent'];
730
+ const visibilityChangeEvent = 'visibilitychange';
731
+ const focusEvent = 'focus';
732
+ const preloadWarning = 'Error preloading route! ☝️';
733
+ class Router {
734
+ #unsubHistory;
735
+ resetNextScroll = false;
736
+ tempLocationKey = `${Math.round(Math.random() * 10000000)}`;
737
+ // nextTemporaryLocation?: ParsedLocation<FullSearchSchema<TRouteTree>>
738
+
739
+ constructor(options) {
740
+ this.options = {
741
+ defaultPreloadDelay: 50,
742
+ context: undefined,
743
+ ...options,
744
+ stringifySearch: options?.stringifySearch ?? defaultStringifySearch,
745
+ parseSearch: options?.parseSearch ?? defaultParseSearch
746
+ // fetchServerDataFn: options?.fetchServerDataFn ?? defaultFetchServerDataFn,
747
+ };
1708
748
 
1709
- cascadeLoaderData(router.state.matches);
1710
- router.listeners.forEach(listener => listener(router));
1711
- },
1712
- dehydrateState: () => {
1713
- return _extends({}, pick(router.state, ['status', 'location', 'lastUpdated']), {
1714
- matches: router.state.matches.map(match => pick(match, ['matchId', 'status', 'routeLoaderData', 'loaderData', 'isInvalid', 'invalidAt']))
1715
- });
1716
- },
1717
- hydrateState: dehydratedState => {
1718
- // Match the routes
1719
- const matches = router.matchRoutes(router.location.pathname, {
1720
- strictParseParams: true
1721
- });
1722
- matches.forEach((match, index) => {
1723
- const dehydratedMatch = dehydratedState.matches[index];
1724
- invariant(dehydratedMatch, 'Oh no! Dehydrated route matches did not match the active state of the router 😬');
1725
- Object.assign(match, dehydratedMatch);
1726
- });
1727
- matches.forEach(match => match.__.validate());
1728
- router.state = _extends({}, router.state, dehydratedState, {
1729
- matches
749
+ this.__store = new Store(getInitialRouterState(), {
750
+ onUpdate: () => {
751
+ const prev = this.state;
752
+ const next = this.__store.state;
753
+ const matchesByIdChanged = prev.matchesById !== next.matchesById;
754
+ let matchesChanged;
755
+ let pendingMatchesChanged;
756
+ if (!matchesByIdChanged) {
757
+ matchesChanged = prev.matchIds.length !== next.matchIds.length || prev.matchIds.some((d, i) => d !== next.matchIds[i]);
758
+ pendingMatchesChanged = prev.pendingMatchIds.length !== next.pendingMatchIds.length || prev.pendingMatchIds.some((d, i) => d !== next.pendingMatchIds[i]);
759
+ }
760
+ if (matchesByIdChanged || matchesChanged) {
761
+ next.matches = next.matchIds.map(id => {
762
+ return next.matchesById[id];
763
+ });
764
+ }
765
+ if (matchesByIdChanged || pendingMatchesChanged) {
766
+ next.pendingMatches = next.pendingMatchIds.map(id => {
767
+ return next.matchesById[id];
768
+ });
769
+ }
770
+ if (matchesByIdChanged || matchesChanged || pendingMatchesChanged) {
771
+ const hasPendingComponent = next.pendingMatches.some(d => {
772
+ const route = this.getRoute(d.routeId);
773
+ return !!route?.options.pendingComponent;
774
+ });
775
+ next.renderedMatchIds = hasPendingComponent ? next.pendingMatchIds : next.matchIds;
776
+ next.renderedMatches = next.renderedMatchIds.map(id => {
777
+ return next.matchesById[id];
778
+ });
779
+ }
780
+ next.isFetching = [...next.matches, ...next.pendingMatches].some(d => d.isFetching);
781
+ this.state = next;
782
+ },
783
+ defaultPriority: 'low'
784
+ });
785
+ this.state = this.__store.state;
786
+ this.update(options);
787
+ const nextLocation = this.buildLocation({
788
+ search: true,
789
+ params: true,
790
+ hash: true,
791
+ state: true
792
+ });
793
+ if (this.state.location.href !== nextLocation.href) {
794
+ this.#commitLocation({
795
+ ...nextLocation,
796
+ replace: true
1730
797
  });
1731
- },
1732
- mount: () => {
1733
- const next = router.__.buildLocation({
1734
- to: '.',
1735
- search: true,
1736
- hash: true
1737
- }); // If the current location isn't updated, trigger a navigation
1738
- // to the current location. Otherwise, load the current location.
1739
-
1740
-
1741
- if (next.href !== router.location.href) {
1742
- router.__.commitLocation(next, true);
1743
- }
1744
-
1745
- if (!router.state.matches.length) {
1746
- router.load();
798
+ }
799
+ }
800
+ subscribers = new Set();
801
+ subscribe = (eventType, fn) => {
802
+ const listener = {
803
+ eventType,
804
+ fn
805
+ };
806
+ this.subscribers.add(listener);
807
+ return () => {
808
+ this.subscribers.delete(listener);
809
+ };
810
+ };
811
+ #emit = routerEvent => {
812
+ this.subscribers.forEach(listener => {
813
+ if (listener.eventType === routerEvent.type) {
814
+ listener.fn(routerEvent);
1747
815
  }
1748
-
1749
- const unsub = router.history.listen(event => {
1750
- router.load(router.__.parseLocation(event.location, router.location));
1751
- }); // addEventListener does not exist in React Native, but window does
1752
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
1753
-
1754
- if (!isServer && window.addEventListener) {
1755
- // Listen to visibillitychange and focus
1756
- window.addEventListener('visibilitychange', router.onFocus, false);
1757
- window.addEventListener('focus', router.onFocus, false);
816
+ });
817
+ };
818
+ reset = () => {
819
+ this.__store.setState(s => Object.assign(s, getInitialRouterState()));
820
+ };
821
+ mount = () => {
822
+ // addEventListener does not exist in React Native, but window does
823
+ // In the future, we might need to invert control here for more adapters
824
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
825
+ if (typeof window !== 'undefined' && window.addEventListener) {
826
+ window.addEventListener(visibilityChangeEvent, this.#onFocus, false);
827
+ window.addEventListener(focusEvent, this.#onFocus, false);
828
+ }
829
+ this.safeLoad();
830
+ return () => {
831
+ if (typeof window !== 'undefined' && window.removeEventListener) {
832
+ window.removeEventListener(visibilityChangeEvent, this.#onFocus);
833
+ window.removeEventListener(focusEvent, this.#onFocus);
1758
834
  }
1759
-
1760
- return () => {
1761
- unsub();
1762
-
1763
- if (!isServer && window.removeEventListener) {
1764
- // Be sure to unsubscribe if a new handler is set
1765
- window.removeEventListener('visibilitychange', router.onFocus);
1766
- window.removeEventListener('focus', router.onFocus);
1767
- }
1768
- };
1769
- },
1770
- onFocus: () => {
1771
- router.load();
1772
- },
1773
- update: opts => {
1774
- const newHistory = (opts == null ? void 0 : opts.history) !== router.history;
1775
-
1776
- if (!router.location || newHistory) {
1777
- if (opts != null && opts.history) {
1778
- router.history = opts.history;
1779
- }
1780
-
1781
- router.location = router.__.parseLocation(router.history.location);
1782
- router.state.location = router.location;
835
+ };
836
+ };
837
+ #onFocus = () => {
838
+ if (this.options.reloadOnWindowFocus ?? true) {
839
+ this.invalidate({
840
+ __fromFocus: true
841
+ });
842
+ }
843
+ };
844
+ update = opts => {
845
+ this.options = {
846
+ ...this.options,
847
+ ...opts,
848
+ context: {
849
+ ...this.options.context,
850
+ ...opts?.context
1783
851
  }
1784
-
1785
- Object.assign(router.options, opts);
1786
- const {
1787
- basepath,
1788
- routeConfig
1789
- } = router.options;
1790
- router.basepath = cleanPath("/" + (basepath != null ? basepath : ''));
1791
-
1792
- if (routeConfig) {
1793
- router.routesById = {};
1794
- router.routeTree = router.__.buildRouteTree(routeConfig);
852
+ };
853
+ if (!this.history || this.options.history && this.options.history !== this.history) {
854
+ if (this.#unsubHistory) {
855
+ this.#unsubHistory();
1795
856
  }
1796
-
1797
- return router;
1798
- },
1799
- cancelMatches: () => {
1800
- var _router$state$pending, _router$state$pending2;
1801
- [...router.state.matches, ...((_router$state$pending = (_router$state$pending2 = router.state.pending) == null ? void 0 : _router$state$pending2.matches) != null ? _router$state$pending : [])].forEach(match => {
1802
- match.cancel();
857
+ this.history = this.options.history ?? (isServer ? createMemoryHistory() : createBrowserHistory());
858
+ const parsedLocation = this.#parseLocation();
859
+ this.__store.setState(s => ({
860
+ ...s,
861
+ resolvedLocation: parsedLocation,
862
+ location: parsedLocation
863
+ }));
864
+ this.#unsubHistory = this.history.subscribe(() => {
865
+ this.safeLoad({
866
+ next: this.#parseLocation(this.state.location)
867
+ });
1803
868
  });
1804
- },
1805
- load: async next => {
1806
- const id = Math.random();
1807
- router.startedLoadingAt = id;
1808
-
1809
- if (next) {
1810
- // Ingest the new location
1811
- router.location = next;
1812
- } // Cancel any pending matches
1813
-
869
+ }
870
+ const {
871
+ basepath,
872
+ routeTree
873
+ } = this.options;
874
+ this.basepath = `/${trimPath(basepath ?? '') ?? ''}`;
875
+ if (routeTree && routeTree !== this.routeTree) {
876
+ this.#processRoutes(routeTree);
877
+ }
878
+ return this;
879
+ };
880
+ cancelMatches = () => {
881
+ this.state.matches.forEach(match => {
882
+ this.cancelMatch(match.id);
883
+ });
884
+ };
885
+ cancelMatch = id => {
886
+ this.getRouteMatch(id)?.abortController?.abort();
887
+ };
888
+ safeLoad = async opts => {
889
+ try {
890
+ return this.load(opts);
891
+ } catch (err) {
892
+ // Don't do anything
893
+ }
894
+ };
895
+ latestLoadPromise = Promise.resolve();
896
+ load = async opts => {
897
+ const promise = new Promise(async (resolve, reject) => {
898
+ const prevLocation = this.state.resolvedLocation;
899
+ const pathDidChange = !!(opts?.next && prevLocation.href !== opts.next.href);
900
+ let latestPromise;
901
+ const checkLatest = () => {
902
+ return this.latestLoadPromise !== promise ? this.latestLoadPromise : undefined;
903
+ };
1814
904
 
1815
- router.cancelMatches(); // Match the routes
905
+ // Cancel any pending matches
1816
906
 
1817
- const matches = router.matchRoutes(router.location.pathname, {
1818
- strictParseParams: true
907
+ let pendingMatches;
908
+ this.#emit({
909
+ type: 'onBeforeLoad',
910
+ from: prevLocation,
911
+ to: opts?.next ?? this.state.location,
912
+ pathChanged: pathDidChange
1819
913
  });
914
+ this.__store.batch(() => {
915
+ if (opts?.next) {
916
+ // Ingest the new location
917
+ this.__store.setState(s => ({
918
+ ...s,
919
+ location: opts.next
920
+ }));
921
+ }
1820
922
 
1821
- if (typeof document !== 'undefined') {
1822
- router.state = _extends({}, router.state, {
1823
- pending: {
1824
- matches: matches,
1825
- location: router.location
1826
- },
1827
- status: 'loading'
923
+ // Match the routes
924
+ pendingMatches = this.matchRoutes(this.state.location.pathname, this.state.location.search, {
925
+ throwOnError: opts?.throwOnError,
926
+ debug: true
1828
927
  });
1829
- } else {
1830
- router.state = _extends({}, router.state, {
1831
- matches: matches,
1832
- location: router.location,
1833
- status: 'loading'
1834
- });
1835
- }
1836
-
1837
- router.notify(); // Load the matches
1838
-
1839
- await router.loadMatches(matches);
1840
-
1841
- if (router.startedLoadingAt !== id) {
1842
- // Ignore side-effects of match loading
1843
- return router.navigationPromise;
1844
- }
1845
-
1846
- const previousMatches = router.state.matches;
1847
- const exiting = [],
1848
- staying = [];
1849
- previousMatches.forEach(d => {
1850
- if (matches.find(dd => dd.matchId === d.matchId)) {
1851
- staying.push(d);
1852
- } else {
1853
- exiting.push(d);
1854
- }
1855
- });
1856
- const entering = matches.filter(d => {
1857
- return !previousMatches.find(dd => dd.matchId === d.matchId);
928
+ this.__store.setState(s => ({
929
+ ...s,
930
+ status: 'pending',
931
+ pendingMatchIds: pendingMatches.map(d => d.id),
932
+ matchesById: this.#mergeMatches(s.matchesById, pendingMatches)
933
+ }));
1858
934
  });
1859
- const now = Date.now();
1860
- exiting.forEach(d => {
1861
- var _ref, _d$options$loaderGcMa, _ref2, _d$options$loaderMaxA;
1862
-
1863
- d.__.onExit == null ? void 0 : d.__.onExit({
1864
- params: d.params,
1865
- search: d.routeSearch
1866
- }); // Clear idle error states when match leaves
1867
-
1868
- if (d.status === 'error' && !d.isFetching) {
1869
- d.status = 'idle';
1870
- d.error = undefined;
935
+ try {
936
+ // Load the matches
937
+ try {
938
+ await this.loadMatches(pendingMatches.map(d => d.id));
939
+ } catch (err) {
940
+ // swallow this error, since we'll display the
941
+ // errors on the route components
1871
942
  }
1872
943
 
1873
- const gc = Math.max((_ref = (_d$options$loaderGcMa = d.options.loaderGcMaxAge) != null ? _d$options$loaderGcMa : router.options.defaultLoaderGcMaxAge) != null ? _ref : 0, (_ref2 = (_d$options$loaderMaxA = d.options.loaderMaxAge) != null ? _d$options$loaderMaxA : router.options.defaultLoaderMaxAge) != null ? _ref2 : 0);
1874
-
1875
- if (gc > 0) {
1876
- router.matchCache[d.matchId] = {
1877
- gc: gc == Infinity ? Number.MAX_SAFE_INTEGER : now + gc,
1878
- match: d
1879
- };
944
+ // Only apply the latest transition
945
+ if (latestPromise = checkLatest()) {
946
+ return latestPromise;
1880
947
  }
1881
- });
1882
- staying.forEach(d => {
1883
- d.options.onTransition == null ? void 0 : d.options.onTransition({
1884
- params: d.params,
1885
- search: d.routeSearch
948
+ const exitingMatchIds = this.state.matchIds.filter(id => !this.state.pendingMatchIds.includes(id));
949
+ const enteringMatchIds = this.state.pendingMatchIds.filter(id => !this.state.matchIds.includes(id));
950
+ const stayingMatchIds = this.state.matchIds.filter(id => this.state.pendingMatchIds.includes(id));
951
+ this.__store.setState(s => ({
952
+ ...s,
953
+ status: 'idle',
954
+ resolvedLocation: s.location,
955
+ matchIds: s.pendingMatchIds,
956
+ pendingMatchIds: []
957
+ }));
958
+ [[exitingMatchIds, 'onLeave'], [enteringMatchIds, 'onEnter'], [stayingMatchIds, 'onTransition']].forEach(([matchIds, hook]) => {
959
+ matchIds.forEach(id => {
960
+ const match = this.getRouteMatch(id);
961
+ const route = this.getRoute(match.routeId);
962
+ route.options[hook]?.(match);
963
+ });
1886
964
  });
1887
- });
1888
- entering.forEach(d => {
1889
- d.__.onExit = d.options.onMatch == null ? void 0 : d.options.onMatch({
1890
- params: d.params,
1891
- search: d.search
965
+ this.#emit({
966
+ type: 'onLoad',
967
+ from: prevLocation,
968
+ to: this.state.location,
969
+ pathChanged: pathDidChange
1892
970
  });
1893
- delete router.matchCache[d.matchId];
1894
- });
1895
-
1896
- if (router.startedLoadingAt !== id) {
1897
- // Ignore side-effects of match loading
1898
- return;
1899
- }
1900
-
1901
- matches.forEach(match => {
1902
- // Clear actions
1903
- if (match.action) {
1904
- match.action.current = undefined;
1905
- match.action.submissions = [];
971
+ resolve();
972
+ } catch (err) {
973
+ // Only apply the latest transition
974
+ if (latestPromise = checkLatest()) {
975
+ return latestPromise;
1906
976
  }
1907
- });
1908
- router.state = _extends({}, router.state, {
1909
- location: router.location,
1910
- matches,
1911
- pending: undefined,
1912
- status: 'idle'
1913
- });
1914
- router.notify();
1915
- router.resolveNavigation();
1916
- },
1917
- cleanMatchCache: () => {
1918
- const now = Date.now();
1919
- Object.keys(router.matchCache).forEach(matchId => {
1920
- const entry = router.matchCache[matchId]; // Don't remove loading matches
1921
-
1922
- if (entry.match.status === 'loading') {
1923
- return;
1924
- } // Do not remove successful matches that are still valid
1925
-
1926
-
1927
- if (entry.gc > 0 && entry.gc > now) {
1928
- return;
1929
- } // Everything else gets removed
1930
-
1931
-
1932
- delete router.matchCache[matchId];
1933
- });
1934
- },
1935
- loadRoute: async function loadRoute(navigateOpts) {
1936
- if (navigateOpts === void 0) {
1937
- navigateOpts = router.location;
977
+ reject(err);
1938
978
  }
1939
-
1940
- const next = router.buildNext(navigateOpts);
1941
- const matches = router.matchRoutes(next.pathname, {
1942
- strictParseParams: true
1943
- });
1944
- await router.loadMatches(matches);
1945
- return matches;
1946
- },
1947
- preloadRoute: async function preloadRoute(navigateOpts, loaderOpts) {
1948
- var _ref3, _ref4, _loaderOpts$maxAge, _ref5, _ref6, _loaderOpts$gcMaxAge;
1949
-
1950
- if (navigateOpts === void 0) {
1951
- navigateOpts = router.location;
979
+ });
980
+ this.latestLoadPromise = promise;
981
+ this.latestLoadPromise.then(() => {
982
+ this.cleanMatches();
983
+ });
984
+ return this.latestLoadPromise;
985
+ };
986
+ #mergeMatches = (prevMatchesById, nextMatches) => {
987
+ let matchesById = {
988
+ ...prevMatchesById
989
+ };
990
+ nextMatches.forEach(match => {
991
+ if (!matchesById[match.id]) {
992
+ matchesById[match.id] = match;
1952
993
  }
1953
-
1954
- const next = router.buildNext(navigateOpts);
1955
- const matches = router.matchRoutes(next.pathname, {
1956
- strictParseParams: true
994
+ matchesById[match.id] = {
995
+ ...matchesById[match.id],
996
+ ...match
997
+ };
998
+ });
999
+ return matchesById;
1000
+ };
1001
+ getRoute = id => {
1002
+ const route = this.routesById[id];
1003
+ invariant(route, `Route with id "${id}" not found`);
1004
+ return route;
1005
+ };
1006
+ preloadRoute = async (navigateOpts = this.state.location) => {
1007
+ let next = this.buildLocation(navigateOpts);
1008
+ const matches = this.matchRoutes(next.pathname, next.search, {
1009
+ throwOnError: true
1010
+ });
1011
+ this.__store.setState(s => {
1012
+ return {
1013
+ ...s,
1014
+ matchesById: this.#mergeMatches(s.matchesById, matches)
1015
+ };
1016
+ });
1017
+ await this.loadMatches(matches.map(d => d.id), {
1018
+ preload: true,
1019
+ maxAge: navigateOpts.maxAge
1020
+ });
1021
+ return [last(matches), matches];
1022
+ };
1023
+ cleanMatches = () => {
1024
+ const now = Date.now();
1025
+ const outdatedMatchIds = Object.values(this.state.matchesById).filter(match => {
1026
+ const route = this.getRoute(match.routeId);
1027
+ 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);
1028
+ }).map(d => d.id);
1029
+ if (outdatedMatchIds.length) {
1030
+ this.__store.setState(s => {
1031
+ const matchesById = {
1032
+ ...s.matchesById
1033
+ };
1034
+ outdatedMatchIds.forEach(id => {
1035
+ delete matchesById[id];
1036
+ });
1037
+ return {
1038
+ ...s,
1039
+ matchesById
1040
+ };
1957
1041
  });
1958
- await router.loadMatches(matches, {
1959
- preload: true,
1960
- maxAge: (_ref3 = (_ref4 = (_loaderOpts$maxAge = loaderOpts.maxAge) != null ? _loaderOpts$maxAge : router.options.defaultPreloadMaxAge) != null ? _ref4 : router.options.defaultLoaderMaxAge) != null ? _ref3 : 0,
1961
- gcMaxAge: (_ref5 = (_ref6 = (_loaderOpts$gcMaxAge = loaderOpts.gcMaxAge) != null ? _loaderOpts$gcMaxAge : router.options.defaultPreloadGcMaxAge) != null ? _ref6 : router.options.defaultLoaderGcMaxAge) != null ? _ref5 : 0
1042
+ }
1043
+ };
1044
+ matchRoutes = (pathname, locationSearch, opts) => {
1045
+ let routeParams = {};
1046
+ let foundRoute = this.flatRoutes.find(route => {
1047
+ const matchedParams = matchPathname(this.basepath, trimPathRight(pathname), {
1048
+ to: route.fullPath,
1049
+ caseSensitive: route.options.caseSensitive ?? this.options.caseSensitive
1962
1050
  });
1963
- return matches;
1964
- },
1965
- matchRoutes: (pathname, opts) => {
1966
- var _router$state$pending3, _router$state$pending4;
1967
-
1968
- router.cleanMatchCache();
1969
- const matches = [];
1970
-
1971
- if (!router.routeTree) {
1972
- return matches;
1051
+ if (matchedParams) {
1052
+ routeParams = matchedParams;
1053
+ return true;
1973
1054
  }
1055
+ return false;
1056
+ });
1057
+ let routeCursor = foundRoute || this.routesById['__root__'];
1058
+ let matchedRoutes = [routeCursor];
1059
+ // let includingLayouts = true
1060
+ while (routeCursor?.parentRoute) {
1061
+ routeCursor = routeCursor.parentRoute;
1062
+ if (routeCursor) matchedRoutes.unshift(routeCursor);
1063
+ }
1974
1064
 
1975
- const existingMatches = [...router.state.matches, ...((_router$state$pending3 = (_router$state$pending4 = router.state.pending) == null ? void 0 : _router$state$pending4.matches) != null ? _router$state$pending3 : [])];
1065
+ // Existing matches are matches that are already loaded along with
1066
+ // pending matches that are still loading
1976
1067
 
1977
- const recurse = async routes => {
1978
- var _parentMatch$params, _router$options$filte, _foundRoute$childRout;
1068
+ const parseErrors = matchedRoutes.map(route => {
1069
+ let parsedParamsError;
1070
+ if (route.options.parseParams) {
1071
+ try {
1072
+ const parsedParams = route.options.parseParams(routeParams);
1073
+ // Add the parsed params to the accumulated params bag
1074
+ Object.assign(routeParams, parsedParams);
1075
+ } catch (err) {
1076
+ parsedParamsError = new PathParamError(err.message, {
1077
+ cause: err
1078
+ });
1079
+ if (opts?.throwOnError) {
1080
+ throw parsedParamsError;
1081
+ }
1082
+ return parsedParamsError;
1083
+ }
1084
+ }
1085
+ return;
1086
+ });
1087
+ const matches = matchedRoutes.map((route, index) => {
1088
+ const interpolatedPath = interpolatePath(route.path, routeParams);
1089
+ const key = route.options.key ? route.options.key({
1090
+ params: routeParams,
1091
+ search: locationSearch
1092
+ }) ?? '' : '';
1093
+ const stringifiedKey = key ? JSON.stringify(key) : '';
1094
+ const matchId = interpolatePath(route.id, routeParams, true) + stringifiedKey;
1095
+
1096
+ // Waste not, want not. If we already have a match for this route,
1097
+ // reuse it. This is important for layout routes, which might stick
1098
+ // around between navigation actions that only change leaf routes.
1099
+ const existingMatch = this.getRouteMatch(matchId);
1100
+ if (existingMatch) {
1101
+ return {
1102
+ ...existingMatch
1103
+ };
1104
+ }
1979
1105
 
1980
- const parentMatch = last(matches);
1981
- let params = (_parentMatch$params = parentMatch == null ? void 0 : parentMatch.params) != null ? _parentMatch$params : {};
1982
- const filteredRoutes = (_router$options$filte = router.options.filterRoutes == null ? void 0 : router.options.filterRoutes(routes)) != null ? _router$options$filte : routes;
1983
- let foundRoutes = [];
1106
+ // Create a fresh route match
1107
+ const hasLoaders = !!(route.options.loader || componentTypes.some(d => route.options[d]?.preload));
1108
+ const routeMatch = {
1109
+ id: matchId,
1110
+ key: stringifiedKey,
1111
+ routeId: route.id,
1112
+ params: routeParams,
1113
+ pathname: joinPaths([this.basepath, interpolatedPath]),
1114
+ updatedAt: Date.now(),
1115
+ maxAge: -1,
1116
+ preloadMaxAge: -1,
1117
+ routeSearch: {},
1118
+ search: {},
1119
+ status: hasLoaders ? 'pending' : 'success',
1120
+ isFetching: false,
1121
+ invalid: false,
1122
+ error: undefined,
1123
+ paramsError: parseErrors[index],
1124
+ searchError: undefined,
1125
+ loaderData: undefined,
1126
+ loadPromise: Promise.resolve(),
1127
+ routeContext: undefined,
1128
+ context: undefined,
1129
+ abortController: new AbortController(),
1130
+ fetchedAt: 0
1131
+ };
1132
+ return routeMatch;
1133
+ });
1984
1134
 
1985
- const findMatchInRoutes = (parentRoutes, routes) => {
1986
- routes.some(route => {
1987
- var _route$childRoutes, _route$childRoutes2, _route$options$caseSe;
1135
+ // Take each match and resolve its search params and context
1136
+ // This has to happen after the matches are created or found
1137
+ // so that we can use the parent match's search params and context
1138
+ matches.forEach((match, i) => {
1139
+ const parentMatch = matches[i - 1];
1140
+ const route = this.getRoute(match.routeId);
1141
+ const searchInfo = (() => {
1142
+ // Validate the search params and stabilize them
1143
+ const parentSearchInfo = {
1144
+ search: parentMatch?.search ?? locationSearch,
1145
+ routeSearch: parentMatch?.routeSearch ?? locationSearch
1146
+ };
1147
+ try {
1148
+ const validator = typeof route.options.validateSearch === 'object' ? route.options.validateSearch.parse : route.options.validateSearch;
1149
+ let routeSearch = validator?.(parentSearchInfo.search) ?? {};
1150
+ let search = {
1151
+ ...parentSearchInfo.search,
1152
+ ...routeSearch
1153
+ };
1154
+ routeSearch = replaceEqualDeep(match.routeSearch, routeSearch);
1155
+ search = replaceEqualDeep(match.search, search);
1156
+ return {
1157
+ routeSearch,
1158
+ search,
1159
+ searchDidChange: match.routeSearch !== routeSearch
1160
+ };
1161
+ } catch (err) {
1162
+ match.searchError = new SearchParamError(err.message, {
1163
+ cause: err
1164
+ });
1165
+ if (opts?.throwOnError) {
1166
+ throw match.searchError;
1167
+ }
1168
+ return parentSearchInfo;
1169
+ }
1170
+ })();
1171
+ Object.assign(match, searchInfo);
1172
+ });
1173
+ return matches;
1174
+ };
1175
+ loadMatches = async (matchIds, opts) => {
1176
+ const getFreshMatches = () => matchIds.map(d => this.getRouteMatch(d));
1177
+ if (!opts?.preload) {
1178
+ getFreshMatches().forEach(match => {
1179
+ // Update each match with its latest route data
1180
+ this.setRouteMatch(match.id, s => ({
1181
+ ...s,
1182
+ routeSearch: match.routeSearch,
1183
+ search: match.search,
1184
+ routeContext: match.routeContext,
1185
+ context: match.context,
1186
+ error: match.error,
1187
+ paramsError: match.paramsError,
1188
+ searchError: match.searchError,
1189
+ params: match.params,
1190
+ preloadMaxAge: 0
1191
+ }));
1192
+ });
1193
+ }
1194
+ let firstBadMatchIndex;
1988
1195
 
1989
- if (!route.routePath && (_route$childRoutes = route.childRoutes) != null && _route$childRoutes.length) {
1990
- return findMatchInRoutes([...foundRoutes, route], route.childRoutes);
1196
+ // Check each match middleware to see if the route can be accessed
1197
+ try {
1198
+ for (const [index, match] of getFreshMatches().entries()) {
1199
+ const parentMatch = getFreshMatches()[index - 1];
1200
+ const route = this.getRoute(match.routeId);
1201
+ const handleError = (err, code) => {
1202
+ err.routerCode = code;
1203
+ firstBadMatchIndex = firstBadMatchIndex ?? index;
1204
+ if (isRedirect(err)) {
1205
+ throw err;
1206
+ }
1207
+ try {
1208
+ route.options.onError?.(err);
1209
+ } catch (errorHandlerErr) {
1210
+ err = errorHandlerErr;
1211
+ if (isRedirect(errorHandlerErr)) {
1212
+ throw errorHandlerErr;
1991
1213
  }
1214
+ }
1215
+ this.setRouteMatch(match.id, s => ({
1216
+ ...s,
1217
+ error: err,
1218
+ status: 'error',
1219
+ updatedAt: Date.now()
1220
+ }));
1221
+ };
1222
+ if (match.paramsError) {
1223
+ handleError(match.paramsError, 'PARSE_PARAMS');
1224
+ }
1225
+ if (match.searchError) {
1226
+ handleError(match.searchError, 'VALIDATE_SEARCH');
1227
+ }
1228
+ let didError = false;
1229
+ try {
1230
+ const routeContext = (await route.options.beforeLoad?.({
1231
+ ...match,
1232
+ preload: !!opts?.preload,
1233
+ parentContext: parentMatch?.routeContext ?? {},
1234
+ context: parentMatch?.context ?? this?.options.context ?? {}
1235
+ })) ?? {};
1236
+ const context = {
1237
+ ...(parentMatch?.context ?? this?.options.context),
1238
+ ...routeContext
1239
+ };
1240
+ this.setRouteMatch(match.id, s => ({
1241
+ ...s,
1242
+ context,
1243
+ routeContext
1244
+ }));
1245
+ } catch (err) {
1246
+ handleError(err, 'BEFORE_LOAD');
1247
+ didError = true;
1248
+ }
1992
1249
 
1993
- const fuzzy = !!(route.routePath !== '/' || (_route$childRoutes2 = route.childRoutes) != null && _route$childRoutes2.length);
1994
- const matchParams = matchPathname(pathname, {
1995
- to: route.fullPath,
1996
- fuzzy,
1997
- caseSensitive: (_route$options$caseSe = route.options.caseSensitive) != null ? _route$options$caseSe : router.options.caseSensitive
1998
- });
1999
-
2000
- if (matchParams) {
2001
- let parsedParams;
2002
-
2003
- try {
2004
- var _route$options$parseP;
2005
-
2006
- parsedParams = (_route$options$parseP = route.options.parseParams == null ? void 0 : route.options.parseParams(matchParams)) != null ? _route$options$parseP : matchParams;
2007
- } catch (err) {
2008
- if (opts != null && opts.strictParseParams) {
2009
- throw err;
2010
- }
2011
- }
2012
-
2013
- params = _extends({}, params, parsedParams);
1250
+ // If we errored, do not run the next matches' middleware
1251
+ if (didError) {
1252
+ break;
1253
+ }
1254
+ }
1255
+ } catch (err) {
1256
+ if (isRedirect(err)) {
1257
+ if (!opts?.preload) this.navigate(err);
1258
+ return;
1259
+ }
1260
+ throw err;
1261
+ }
1262
+ const validResolvedMatches = getFreshMatches().slice(0, firstBadMatchIndex);
1263
+ const matchPromises = [];
1264
+ validResolvedMatches.forEach((match, index) => {
1265
+ matchPromises.push((async () => {
1266
+ const parentMatchPromise = matchPromises[index - 1];
1267
+ const route = this.getRoute(match.routeId);
1268
+ if (match.isFetching || match.status === 'success' && !isMatchInvalid(match, {
1269
+ preload: opts?.preload
1270
+ })) {
1271
+ return this.getRouteMatch(match.id)?.loadPromise;
1272
+ }
1273
+ const fetchedAt = Date.now();
1274
+ const checkLatest = () => {
1275
+ const latest = this.getRouteMatch(match.id);
1276
+ return latest && latest.fetchedAt !== fetchedAt ? latest.loadPromise : undefined;
1277
+ };
1278
+ const handleIfRedirect = err => {
1279
+ if (isRedirect(err)) {
1280
+ if (!opts?.preload) {
1281
+ this.navigate(err);
2014
1282
  }
2015
-
2016
- if (!!matchParams) {
2017
- foundRoutes = [...parentRoutes, route];
1283
+ return true;
1284
+ }
1285
+ return false;
1286
+ };
1287
+ const load = async () => {
1288
+ let latestPromise;
1289
+ try {
1290
+ const componentsPromise = Promise.all(componentTypes.map(async type => {
1291
+ const component = route.options[type];
1292
+ if (component?.preload) {
1293
+ await component.preload();
1294
+ }
1295
+ }));
1296
+ const loaderPromise = route.options.loader?.({
1297
+ ...match,
1298
+ preload: !!opts?.preload,
1299
+ parentMatchPromise
1300
+ });
1301
+ const [_, loader] = await Promise.all([componentsPromise, loaderPromise]);
1302
+ if (latestPromise = checkLatest()) return await latestPromise;
1303
+ this.setRouteMatchData(match.id, () => loader, opts);
1304
+ } catch (error) {
1305
+ if (latestPromise = checkLatest()) return await latestPromise;
1306
+ if (handleIfRedirect(error)) return;
1307
+ try {
1308
+ route.options.onError?.(error);
1309
+ } catch (onErrorError) {
1310
+ error = onErrorError;
1311
+ if (handleIfRedirect(onErrorError)) return;
2018
1312
  }
2019
-
2020
- return !!foundRoutes.length;
2021
- });
2022
- return !!foundRoutes.length;
1313
+ this.setRouteMatch(match.id, s => ({
1314
+ ...s,
1315
+ error,
1316
+ status: 'error',
1317
+ isFetching: false,
1318
+ updatedAt: Date.now()
1319
+ }));
1320
+ }
2023
1321
  };
1322
+ let loadPromise;
1323
+ this.__store.batch(() => {
1324
+ this.setRouteMatch(match.id, s => ({
1325
+ ...s,
1326
+ // status: s.status !== 'success' ? 'pending' : s.status,
1327
+ isFetching: true,
1328
+ fetchedAt,
1329
+ invalid: false
1330
+ }));
1331
+ loadPromise = load();
1332
+ this.setRouteMatch(match.id, s => ({
1333
+ ...s,
1334
+ loadPromise
1335
+ }));
1336
+ });
1337
+ await loadPromise;
1338
+ })());
1339
+ });
1340
+ await Promise.all(matchPromises);
1341
+ };
1342
+ resolvePath = (from, path) => {
1343
+ return resolvePath(this.basepath, from, cleanPath(path));
1344
+ };
1345
+ navigate = async ({
1346
+ from,
1347
+ to = '',
1348
+ ...rest
1349
+ }) => {
1350
+ // If this link simply reloads the current route,
1351
+ // make sure it has a new key so it will trigger a data refresh
1352
+
1353
+ // If this `to` is a valid external URL, return
1354
+ // null for LinkUtils
1355
+ const toString = String(to);
1356
+ const fromString = typeof from === 'undefined' ? from : String(from);
1357
+ let isExternal;
1358
+ try {
1359
+ new URL(`${toString}`);
1360
+ isExternal = true;
1361
+ } catch (e) {}
1362
+ invariant(!isExternal, 'Attempting to navigate to external url with this.navigate!');
1363
+ return this.#buildAndCommitLocation({
1364
+ ...rest,
1365
+ from: fromString,
1366
+ to: toString
1367
+ });
1368
+ };
1369
+ matchRoute = (location, opts) => {
1370
+ location = {
1371
+ ...location,
1372
+ to: location.to ? this.resolvePath(location.from ?? '', location.to) : undefined
1373
+ };
1374
+ const next = this.buildLocation(location);
1375
+ if (opts?.pending && this.state.status !== 'pending') {
1376
+ return false;
1377
+ }
1378
+ const baseLocation = opts?.pending ? this.state.location : this.state.resolvedLocation;
1379
+ if (!baseLocation) {
1380
+ return false;
1381
+ }
1382
+ const match = matchPathname(this.basepath, baseLocation.pathname, {
1383
+ ...opts,
1384
+ to: next.pathname
1385
+ });
1386
+ if (!match) {
1387
+ return false;
1388
+ }
1389
+ if (opts?.includeSearch ?? true) {
1390
+ return partialDeepEqual(baseLocation.search, next.search) ? match : false;
1391
+ }
1392
+ return match;
1393
+ };
1394
+ buildLink = dest => {
1395
+ // If this link simply reloads the current route,
1396
+ // make sure it has a new key so it will trigger a data refresh
1397
+
1398
+ // If this `to` is a valid external URL, return
1399
+ // null for LinkUtils
1400
+
1401
+ const {
1402
+ to,
1403
+ preload: userPreload,
1404
+ preloadDelay: userPreloadDelay,
1405
+ activeOptions,
1406
+ disabled,
1407
+ target,
1408
+ replace,
1409
+ resetScroll
1410
+ } = dest;
1411
+ try {
1412
+ new URL(`${to}`);
1413
+ return {
1414
+ type: 'external',
1415
+ href: to
1416
+ };
1417
+ } catch (e) {}
1418
+ const nextOpts = dest;
1419
+ const next = this.buildLocation(nextOpts);
1420
+ const preload = userPreload ?? this.options.defaultPreload;
1421
+ const preloadDelay = userPreloadDelay ?? this.options.defaultPreloadDelay ?? 0;
1422
+
1423
+ // Compare path/hash for matches
1424
+ const currentPathSplit = this.state.location.pathname.split('/');
1425
+ const nextPathSplit = next.pathname.split('/');
1426
+ const pathIsFuzzyEqual = nextPathSplit.every((d, i) => d === currentPathSplit[i]);
1427
+ // Combine the matches based on user options
1428
+ const pathTest = activeOptions?.exact ? this.state.location.pathname === next.pathname : pathIsFuzzyEqual;
1429
+ const hashTest = activeOptions?.includeHash ? this.state.location.hash === next.hash : true;
1430
+ const searchTest = activeOptions?.includeSearch ?? true ? partialDeepEqual(this.state.location.search, next.search) : true;
1431
+
1432
+ // The final "active" test
1433
+ const isActive = pathTest && hashTest && searchTest;
1434
+
1435
+ // The click handler
1436
+ const handleClick = e => {
1437
+ if (!disabled && !isCtrlEvent(e) && !e.defaultPrevented && (!target || target === '_self') && e.button === 0) {
1438
+ e.preventDefault();
1439
+
1440
+ // All is well? Navigate!
1441
+ this.#commitLocation({
1442
+ ...next,
1443
+ replace,
1444
+ resetScroll
1445
+ });
1446
+ }
1447
+ };
2024
1448
 
2025
- findMatchInRoutes([], filteredRoutes);
2026
-
2027
- if (!foundRoutes.length) {
1449
+ // The click handler
1450
+ const handleFocus = e => {
1451
+ if (preload) {
1452
+ this.preloadRoute(nextOpts).catch(err => {
1453
+ console.warn(err);
1454
+ console.warn(preloadWarning);
1455
+ });
1456
+ }
1457
+ };
1458
+ const handleTouchStart = e => {
1459
+ this.preloadRoute(nextOpts).catch(err => {
1460
+ console.warn(err);
1461
+ console.warn(preloadWarning);
1462
+ });
1463
+ };
1464
+ const handleEnter = e => {
1465
+ const target = e.target || {};
1466
+ if (preload) {
1467
+ if (target.preloadTimeout) {
2028
1468
  return;
2029
1469
  }
2030
-
2031
- foundRoutes.forEach(foundRoute => {
2032
- var _router$matchCache$ma;
2033
-
2034
- const interpolatedPath = interpolatePath(foundRoute.routePath, params);
2035
- const matchId = interpolatePath(foundRoute.routeId, params, true);
2036
- const match = existingMatches.find(d => d.matchId === matchId) || ((_router$matchCache$ma = router.matchCache[matchId]) == null ? void 0 : _router$matchCache$ma.match) || createRouteMatch(router, foundRoute, {
2037
- parentMatch,
2038
- matchId,
2039
- params,
2040
- pathname: joinPaths([pathname, interpolatedPath])
1470
+ target.preloadTimeout = setTimeout(() => {
1471
+ target.preloadTimeout = null;
1472
+ this.preloadRoute(nextOpts).catch(err => {
1473
+ console.warn(err);
1474
+ console.warn(preloadWarning);
2041
1475
  });
2042
- matches.push(match);
2043
- });
2044
- const foundRoute = last(foundRoutes);
2045
-
2046
- if ((_foundRoute$childRout = foundRoute.childRoutes) != null && _foundRoute$childRout.length) {
2047
- recurse(foundRoute.childRoutes);
2048
- }
1476
+ }, preloadDelay);
1477
+ }
1478
+ };
1479
+ const handleLeave = e => {
1480
+ const target = e.target || {};
1481
+ if (target.preloadTimeout) {
1482
+ clearTimeout(target.preloadTimeout);
1483
+ target.preloadTimeout = null;
1484
+ }
1485
+ };
1486
+ return {
1487
+ type: 'internal',
1488
+ next,
1489
+ handleFocus,
1490
+ handleClick,
1491
+ handleEnter,
1492
+ handleLeave,
1493
+ handleTouchStart,
1494
+ isActive,
1495
+ disabled
1496
+ };
1497
+ };
1498
+ dehydrate = () => {
1499
+ return {
1500
+ state: {
1501
+ matchIds: this.state.matchIds,
1502
+ dehydratedMatches: this.state.matches.map(d => pick(d, ['fetchedAt', 'invalid', 'preloadMaxAge', 'maxAge', 'id', 'loaderData', 'status', 'updatedAt']))
1503
+ }
1504
+ };
1505
+ };
1506
+ hydrate = async __do_not_use_server_ctx => {
1507
+ let _ctx = __do_not_use_server_ctx;
1508
+ // Client hydrates from window
1509
+ if (typeof document !== 'undefined') {
1510
+ _ctx = window.__TSR_DEHYDRATED__;
1511
+ }
1512
+ invariant(_ctx, 'Expected to find a __TSR_DEHYDRATED__ property on window... but we did not. Did you forget to render <DehydrateRouter /> in your app?');
1513
+ const ctx = _ctx;
1514
+ this.dehydratedData = ctx.payload;
1515
+ this.options.hydrate?.(ctx.payload);
1516
+ const dehydratedState = ctx.router.state;
1517
+ let matches = this.matchRoutes(this.state.location.pathname, this.state.location.search).map(match => {
1518
+ const dehydratedMatch = dehydratedState.dehydratedMatches.find(d => d.id === match.id);
1519
+ invariant(dehydratedMatch, `Could not find a client-side match for dehydrated match with id: ${match.id}!`);
1520
+ if (dehydratedMatch) {
1521
+ return {
1522
+ ...match,
1523
+ ...dehydratedMatch
1524
+ };
1525
+ }
1526
+ return match;
1527
+ });
1528
+ this.__store.setState(s => {
1529
+ return {
1530
+ ...s,
1531
+ matchIds: dehydratedState.matchIds,
1532
+ matches,
1533
+ matchesById: this.#mergeMatches(s.matchesById, matches)
2049
1534
  };
1535
+ });
1536
+ };
1537
+ injectedHtml = [];
1538
+ injectHtml = async html => {
1539
+ this.injectedHtml.push(html);
1540
+ };
1541
+ dehydrateData = (key, getData) => {
1542
+ if (typeof document === 'undefined') {
1543
+ const strKey = typeof key === 'string' ? key : JSON.stringify(key);
1544
+ this.injectHtml(async () => {
1545
+ const id = `__TSR_DEHYDRATED__${strKey}`;
1546
+ const data = typeof getData === 'function' ? await getData() : getData;
1547
+ return `<script id='${id}' suppressHydrationWarning>window["__TSR_DEHYDRATED__${escapeJSON(strKey)}"] = ${JSON.stringify(data)}
1548
+ ;(() => {
1549
+ var el = document.getElementById('${id}')
1550
+ el.parentElement.removeChild(el)
1551
+ })()
1552
+ </script>`;
1553
+ });
1554
+ return () => this.hydrateData(key);
1555
+ }
1556
+ return () => undefined;
1557
+ };
1558
+ hydrateData = key => {
1559
+ if (typeof document !== 'undefined') {
1560
+ const strKey = typeof key === 'string' ? key : JSON.stringify(key);
1561
+ return window[`__TSR_DEHYDRATED__${strKey}`];
1562
+ }
1563
+ return undefined;
1564
+ };
2050
1565
 
2051
- recurse([router.routeTree]);
2052
- cascadeLoaderData(matches);
2053
- return matches;
2054
- },
2055
- loadMatches: async (resolvedMatches, loaderOpts) => {
2056
- const matchPromises = resolvedMatches.map(async match => {
2057
- // Validate the match (loads search params etc)
2058
- match.__.validate();
2059
-
2060
- match.load(loaderOpts);
2061
- const search = match.search;
1566
+ // resolveMatchPromise = (matchId: string, key: string, value: any) => {
1567
+ // this.state.matches
1568
+ // .find((d) => d.id === matchId)
1569
+ // ?.__promisesByKey[key]?.resolve(value)
1570
+ // }
2062
1571
 
2063
- if (search.__data && search.__data.matchId !== match.matchId) {
2064
- return;
1572
+ #processRoutes = routeTree => {
1573
+ this.routeTree = routeTree;
1574
+ this.routesById = {};
1575
+ this.routesByPath = {};
1576
+ this.flatRoutes = [];
1577
+ const recurseRoutes = routes => {
1578
+ routes.forEach((route, i) => {
1579
+ route.init({
1580
+ originalIndex: i,
1581
+ router: this
1582
+ });
1583
+ const existingRoute = this.routesById[route.id];
1584
+ invariant(!existingRoute, `Duplicate routes found with id: ${String(route.id)}`);
1585
+ this.routesById[route.id] = route;
1586
+ if (!route.isRoot && route.path) {
1587
+ const trimmedFullPath = trimPathRight(route.fullPath);
1588
+ if (!this.routesByPath[trimmedFullPath] || route.fullPath.endsWith('/')) {
1589
+ this.routesByPath[trimmedFullPath] = route;
1590
+ }
2065
1591
  }
2066
-
2067
- if (match.__.loadPromise) {
2068
- // Wait for the first sign of activity from the match
2069
- await match.__.loadPromise;
1592
+ const children = route.children;
1593
+ if (children?.length) {
1594
+ recurseRoutes(children);
2070
1595
  }
2071
1596
  });
2072
- router.notify();
2073
- await Promise.all(matchPromises);
2074
- },
2075
- loadMatchData: async routeMatch => {
2076
- if (isServer || !router.options.useServerData) {
2077
- var _await$routeMatch$opt;
2078
-
2079
- return (_await$routeMatch$opt = await (routeMatch.options.loader == null ? void 0 : routeMatch.options.loader({
2080
- // parentLoaderPromise: routeMatch.parentMatch?.__.dataPromise,
2081
- params: routeMatch.params,
2082
- search: routeMatch.routeSearch,
2083
- signal: routeMatch.__.abortController.signal
2084
- }))) != null ? _await$routeMatch$opt : {};
2085
- } else {
2086
- const next = router.buildNext({
2087
- to: '.',
2088
- search: d => _extends({}, d != null ? d : {}, {
2089
- __data: {
2090
- matchId: routeMatch.matchId
2091
- }
2092
- })
2093
- });
2094
- const res = await fetch(next.href, {
2095
- method: 'GET' // signal: routeMatch.__.abortController.signal,
2096
-
2097
- });
2098
-
2099
- if (res.ok) {
2100
- return res.json();
2101
- }
2102
-
2103
- throw new Error('Failed to fetch match data');
1597
+ };
1598
+ recurseRoutes([routeTree]);
1599
+ this.flatRoutes = Object.values(this.routesByPath).map((d, i) => {
1600
+ const trimmed = trimPath(d.fullPath);
1601
+ const parsed = parsePathname(trimmed);
1602
+ while (parsed.length > 1 && parsed[0]?.value === '/') {
1603
+ parsed.shift();
2104
1604
  }
2105
- },
2106
- invalidateRoute: opts => {
2107
- var _router$state$pending5, _router$state$pending6;
2108
-
2109
- const next = router.buildNext(opts);
2110
- const unloadedMatchIds = router.matchRoutes(next.pathname).map(d => d.matchId);
2111
- [...router.state.matches, ...((_router$state$pending5 = (_router$state$pending6 = router.state.pending) == null ? void 0 : _router$state$pending6.matches) != null ? _router$state$pending5 : [])].forEach(match => {
2112
- if (unloadedMatchIds.includes(match.matchId)) {
2113
- match.invalidate();
1605
+ const score = parsed.map(d => {
1606
+ if (d.type === 'param') {
1607
+ return 0.5;
2114
1608
  }
2115
- });
2116
- },
2117
- reload: () => router.__.navigate({
2118
- fromCurrent: true,
2119
- replace: true,
2120
- search: true
2121
- }),
2122
- resolvePath: (from, path) => {
2123
- return resolvePath(router.basepath, from, cleanPath(path));
2124
- },
2125
- matchRoute: (location, opts) => {
2126
- var _location$from;
2127
-
2128
- // const location = router.buildNext(opts)
2129
- location = _extends({}, location, {
2130
- to: location.to ? router.resolvePath((_location$from = location.from) != null ? _location$from : '', location.to) : undefined
2131
- });
2132
- const next = router.buildNext(location);
2133
-
2134
- if (opts != null && opts.pending) {
2135
- var _router$state$pending7;
2136
-
2137
- if (!((_router$state$pending7 = router.state.pending) != null && _router$state$pending7.location)) {
2138
- return false;
1609
+ if (d.type === 'wildcard') {
1610
+ return 0.25;
2139
1611
  }
2140
-
2141
- return !!matchPathname(router.state.pending.location.pathname, _extends({}, opts, {
2142
- to: next.pathname
2143
- }));
2144
- }
2145
-
2146
- return !!matchPathname(router.state.location.pathname, _extends({}, opts, {
2147
- to: next.pathname
2148
- }));
2149
- },
2150
- navigate: async _ref7 => {
2151
- let {
2152
- from,
2153
- to = '.',
2154
- search,
2155
- hash,
2156
- replace,
2157
- params
2158
- } = _ref7;
2159
- // If this link simply reloads the current route,
2160
- // make sure it has a new key so it will trigger a data refresh
2161
- // If this `to` is a valid external URL, return
2162
- // null for LinkUtils
2163
- const toString = String(to);
2164
- const fromString = String(from);
2165
- let isExternal;
2166
-
2167
- try {
2168
- new URL("" + toString);
2169
- isExternal = true;
2170
- } catch (e) {}
2171
-
2172
- invariant(!isExternal, 'Attempting to navigate to external url with router.navigate!');
2173
- return router.__.navigate({
2174
- from: fromString,
2175
- to: toString,
2176
- search,
2177
- hash,
2178
- replace,
2179
- params
1612
+ return 1;
2180
1613
  });
2181
- },
2182
- buildLink: _ref8 => {
2183
- var _preload, _ref9;
2184
-
2185
- let {
2186
- from,
2187
- to = '.',
2188
- search,
2189
- params,
2190
- hash,
2191
- target,
2192
- replace,
2193
- activeOptions,
2194
- preload,
2195
- preloadMaxAge: userPreloadMaxAge,
2196
- preloadGcMaxAge: userPreloadGcMaxAge,
2197
- preloadDelay: userPreloadDelay,
2198
- disabled
2199
- } = _ref8;
2200
-
2201
- // If this link simply reloads the current route,
2202
- // make sure it has a new key so it will trigger a data refresh
2203
- // If this `to` is a valid external URL, return
2204
- // null for LinkUtils
2205
- try {
2206
- new URL("" + to);
2207
- return {
2208
- type: 'external',
2209
- href: to
2210
- };
2211
- } catch (e) {}
2212
-
2213
- const nextOpts = {
2214
- from,
2215
- to,
2216
- search,
2217
- params,
2218
- hash,
2219
- replace
1614
+ return {
1615
+ child: d,
1616
+ trimmed,
1617
+ parsed,
1618
+ index: i,
1619
+ score
2220
1620
  };
2221
- const next = router.buildNext(nextOpts);
2222
- preload = (_preload = preload) != null ? _preload : router.options.defaultPreload;
2223
- const preloadDelay = (_ref9 = userPreloadDelay != null ? userPreloadDelay : router.options.defaultPreloadDelay) != null ? _ref9 : 0; // Compare path/hash for matches
2224
-
2225
- const pathIsEqual = router.state.location.pathname === next.pathname;
2226
- const currentPathSplit = router.state.location.pathname.split('/');
2227
- const nextPathSplit = next.pathname.split('/');
2228
- const pathIsFuzzyEqual = nextPathSplit.every((d, i) => d === currentPathSplit[i]);
2229
- const hashIsEqual = router.state.location.hash === next.hash; // Combine the matches based on user options
2230
-
2231
- const pathTest = activeOptions != null && activeOptions.exact ? pathIsEqual : pathIsFuzzyEqual;
2232
- const hashTest = activeOptions != null && activeOptions.includeHash ? hashIsEqual : true; // The final "active" test
2233
-
2234
- const isActive = pathTest && hashTest; // The click handler
2235
-
2236
- const handleClick = e => {
2237
- if (!disabled && !isCtrlEvent(e) && !e.defaultPrevented && (!target || target === '_self') && e.button === 0) {
2238
- e.preventDefault();
2239
-
2240
- if (pathIsEqual && !search && !hash) {
2241
- router.invalidateRoute(nextOpts);
2242
- } // All is well? Navigate!)
2243
-
1621
+ }).sort((a, b) => {
1622
+ let isIndex = a.trimmed === '/' ? 1 : b.trimmed === '/' ? -1 : 0;
1623
+ if (isIndex !== 0) return isIndex;
1624
+ const length = Math.min(a.score.length, b.score.length);
1625
+
1626
+ // Sort by length of score
1627
+ if (a.score.length !== b.score.length) {
1628
+ return b.score.length - a.score.length;
1629
+ }
2244
1630
 
2245
- router.__.navigate(nextOpts);
1631
+ // Sort by min available score
1632
+ for (let i = 0; i < length; i++) {
1633
+ if (a.score[i] !== b.score[i]) {
1634
+ return b.score[i] - a.score[i];
2246
1635
  }
2247
- }; // The click handler
2248
-
1636
+ }
2249
1637
 
2250
- const handleFocus = e => {
2251
- if (preload) {
2252
- router.preloadRoute(nextOpts, {
2253
- maxAge: userPreloadMaxAge,
2254
- gcMaxAge: userPreloadGcMaxAge
2255
- });
1638
+ // Sort by min available parsed value
1639
+ for (let i = 0; i < length; i++) {
1640
+ if (a.parsed[i].value !== b.parsed[i].value) {
1641
+ return a.parsed[i].value > b.parsed[i].value ? 1 : -1;
2256
1642
  }
2257
- };
2258
-
2259
- const handleEnter = e => {
2260
- const target = e.target || {};
1643
+ }
2261
1644
 
2262
- if (preload) {
2263
- if (target.preloadTimeout) {
2264
- return;
2265
- }
1645
+ // Sort by length of trimmed full path
1646
+ if (a.trimmed !== b.trimmed) {
1647
+ return a.trimmed > b.trimmed ? 1 : -1;
1648
+ }
2266
1649
 
2267
- target.preloadTimeout = setTimeout(() => {
2268
- target.preloadTimeout = null;
2269
- router.preloadRoute(nextOpts, {
2270
- maxAge: userPreloadMaxAge,
2271
- gcMaxAge: userPreloadGcMaxAge
2272
- });
2273
- }, preloadDelay);
2274
- }
1650
+ // Sort by original index
1651
+ return a.index - b.index;
1652
+ }).map((d, i) => {
1653
+ d.child.rank = i;
1654
+ return d.child;
1655
+ });
1656
+ };
1657
+ #parseLocation = previousLocation => {
1658
+ const parse = ({
1659
+ pathname,
1660
+ search,
1661
+ hash,
1662
+ state
1663
+ }) => {
1664
+ const parsedSearch = this.options.parseSearch(search);
1665
+ return {
1666
+ pathname: pathname,
1667
+ searchStr: search,
1668
+ search: replaceEqualDeep(previousLocation?.search, parsedSearch),
1669
+ hash: hash.split('#').reverse()[0] ?? '',
1670
+ href: `${pathname}${search}${hash}`,
1671
+ state: replaceEqualDeep(previousLocation?.state, state)
2275
1672
  };
2276
-
2277
- const handleLeave = e => {
2278
- const target = e.target || {};
2279
-
2280
- if (target.preloadTimeout) {
2281
- clearTimeout(target.preloadTimeout);
2282
- target.preloadTimeout = null;
2283
- }
1673
+ };
1674
+ const location = parse(this.history.location);
1675
+ let {
1676
+ __tempLocation,
1677
+ __tempKey
1678
+ } = location.state;
1679
+ if (__tempLocation && (!__tempKey || __tempKey === this.tempLocationKey)) {
1680
+ // Sync up the location keys
1681
+ const parsedTempLocation = parse(__tempLocation);
1682
+ parsedTempLocation.state.key = location.state.key;
1683
+ delete parsedTempLocation.state.__tempLocation;
1684
+ return {
1685
+ ...parsedTempLocation,
1686
+ maskedLocation: location
2284
1687
  };
2285
-
1688
+ }
1689
+ return location;
1690
+ };
1691
+ buildLocation = (opts = {}) => {
1692
+ const build = (dest = {}, matches) => {
1693
+ const from = this.state.location;
1694
+ const fromPathname = dest.from ?? from.pathname;
1695
+ let pathname = resolvePath(this.basepath ?? '/', fromPathname, `${dest.to ?? ''}`);
1696
+ const fromMatches = this.matchRoutes(fromPathname, from.search);
1697
+ const prevParams = {
1698
+ ...last(fromMatches)?.params
1699
+ };
1700
+ let nextParams = (dest.params ?? true) === true ? prevParams : functionalUpdate(dest.params, prevParams);
1701
+ if (nextParams) {
1702
+ matches?.map(d => this.getRoute(d.routeId).options.stringifyParams).filter(Boolean).forEach(fn => {
1703
+ nextParams = {
1704
+ ...nextParams,
1705
+ ...fn(nextParams)
1706
+ };
1707
+ });
1708
+ }
1709
+ pathname = interpolatePath(pathname, nextParams ?? {});
1710
+ const preSearchFilters = matches?.map(match => this.getRoute(match.routeId).options.preSearchFilters ?? []).flat().filter(Boolean) ?? [];
1711
+ const postSearchFilters = matches?.map(match => this.getRoute(match.routeId).options.postSearchFilters ?? []).flat().filter(Boolean) ?? [];
1712
+
1713
+ // Pre filters first
1714
+ const preFilteredSearch = preSearchFilters?.length ? preSearchFilters?.reduce((prev, next) => next(prev), from.search) : from.search;
1715
+
1716
+ // Then the link/navigate function
1717
+ const destSearch = dest.search === true ? preFilteredSearch // Preserve resolvedFrom true
1718
+ : dest.search ? functionalUpdate(dest.search, preFilteredSearch) ?? {} // Updater
1719
+ : preSearchFilters?.length ? preFilteredSearch // Preserve resolvedFrom filters
1720
+ : {};
1721
+
1722
+ // Then post filters
1723
+ const postFilteredSearch = postSearchFilters?.length ? postSearchFilters.reduce((prev, next) => next(prev), destSearch) : destSearch;
1724
+ const search = replaceEqualDeep(from.search, postFilteredSearch);
1725
+ const searchStr = this.options.stringifySearch(search);
1726
+ const hash = dest.hash === true ? from.hash : dest.hash ? functionalUpdate(dest.hash, from.hash) : from.hash;
1727
+ const hashStr = hash ? `#${hash}` : '';
1728
+ let nextState = dest.state === true ? from.state : dest.state ? functionalUpdate(dest.state, from.state) : from.state;
1729
+ nextState = replaceEqualDeep(from.state, nextState);
2286
1730
  return {
2287
- type: 'internal',
2288
- next,
2289
- handleFocus,
2290
- handleClick,
2291
- handleEnter,
2292
- handleLeave,
2293
- isActive,
2294
- disabled
1731
+ pathname,
1732
+ search,
1733
+ searchStr,
1734
+ state: nextState,
1735
+ hash,
1736
+ href: this.history.createHref(`${pathname}${searchStr}${hashStr}`),
1737
+ unmaskOnReload: dest.unmaskOnReload
2295
1738
  };
2296
- },
2297
- buildNext: opts => {
2298
- const next = router.__.buildLocation(opts);
2299
-
2300
- const matches = router.matchRoutes(next.pathname);
2301
-
2302
- const __preSearchFilters = matches.map(match => {
2303
- var _match$options$preSea;
2304
-
2305
- return (_match$options$preSea = match.options.preSearchFilters) != null ? _match$options$preSea : [];
2306
- }).flat().filter(Boolean);
2307
-
2308
- const __postSearchFilters = matches.map(match => {
2309
- var _match$options$postSe;
2310
-
2311
- return (_match$options$postSe = match.options.postSearchFilters) != null ? _match$options$postSe : [];
2312
- }).flat().filter(Boolean);
2313
-
2314
- return router.__.buildLocation(_extends({}, opts, {
2315
- __preSearchFilters,
2316
- __postSearchFilters
2317
- }));
2318
- },
2319
- __: {
2320
- buildRouteTree: rootRouteConfig => {
2321
- const recurseRoutes = (routeConfigs, parent) => {
2322
- return routeConfigs.map(routeConfig => {
2323
- const routeOptions = routeConfig.options;
2324
- const route = createRoute(routeConfig, routeOptions, parent, router);
2325
- const existingRoute = router.routesById[route.routeId];
2326
-
2327
- if (existingRoute) {
2328
- if (process.env.NODE_ENV !== 'production') {
2329
- console.warn("Duplicate routes found with id: " + String(route.routeId), router.routesById, route);
2330
- }
2331
-
2332
- throw new Error();
2333
- }
2334
- router.routesById[route.routeId] = route;
2335
- const children = routeConfig.children;
2336
- route.childRoutes = children != null && children.length ? recurseRoutes(children, route) : undefined;
2337
- return route;
1739
+ };
1740
+ const buildWithMatches = (dest = {}, maskedDest) => {
1741
+ let next = build(dest);
1742
+ let maskedNext = maskedDest ? build(maskedDest) : undefined;
1743
+ if (!maskedNext) {
1744
+ let params = {};
1745
+ let foundMask = this.options.routeMasks?.find(d => {
1746
+ const match = matchPathname(this.basepath, next.pathname, {
1747
+ to: d.from,
1748
+ fuzzy: false
2338
1749
  });
2339
- };
2340
-
2341
- const routes = recurseRoutes([rootRouteConfig]);
2342
- return routes[0];
2343
- },
2344
- parseLocation: (location, previousLocation) => {
2345
- var _location$hash$split$;
2346
-
2347
- const parsedSearch = router.options.parseSearch(location.search);
2348
- return {
2349
- pathname: location.pathname,
2350
- searchStr: location.search,
2351
- search: replaceEqualDeep(previousLocation == null ? void 0 : previousLocation.search, parsedSearch),
2352
- hash: (_location$hash$split$ = location.hash.split('#').reverse()[0]) != null ? _location$hash$split$ : '',
2353
- href: "" + location.pathname + location.search + location.hash,
2354
- state: location.state,
2355
- key: location.key
2356
- };
2357
- },
2358
- navigate: location => {
2359
- const next = router.buildNext(location);
2360
- return router.__.commitLocation(next, location.replace);
2361
- },
2362
- buildLocation: function buildLocation(dest) {
2363
- var _dest$from, _router$basepath, _dest$to, _last, _dest$params, _dest$__preSearchFilt, _functionalUpdate, _dest$__preSearchFilt2, _dest$__postSearchFil;
2364
-
2365
- if (dest === void 0) {
2366
- dest = {};
2367
- }
2368
-
2369
- // const resolvedFrom: Location = {
2370
- // ...router.location,
2371
- const fromPathname = dest.fromCurrent ? router.location.pathname : (_dest$from = dest.from) != null ? _dest$from : router.location.pathname;
2372
-
2373
- let pathname = resolvePath((_router$basepath = router.basepath) != null ? _router$basepath : '/', fromPathname, "" + ((_dest$to = dest.to) != null ? _dest$to : '.'));
2374
-
2375
- const fromMatches = router.matchRoutes(router.location.pathname, {
2376
- strictParseParams: true
1750
+ if (match) {
1751
+ params = match;
1752
+ return true;
1753
+ }
1754
+ return false;
2377
1755
  });
2378
- const toMatches = router.matchRoutes(pathname);
2379
-
2380
- const prevParams = _extends({}, (_last = last(fromMatches)) == null ? void 0 : _last.params);
2381
-
2382
- let nextParams = ((_dest$params = dest.params) != null ? _dest$params : true) === true ? prevParams : functionalUpdate(dest.params, prevParams);
2383
-
2384
- if (nextParams) {
2385
- toMatches.map(d => d.options.stringifyParams).filter(Boolean).forEach(fn => {
2386
- Object.assign({}, nextParams, fn(nextParams));
2387
- });
2388
- }
2389
-
2390
- pathname = interpolatePath(pathname, nextParams != null ? nextParams : {}); // Pre filters first
2391
-
2392
- const preFilteredSearch = (_dest$__preSearchFilt = dest.__preSearchFilters) != null && _dest$__preSearchFilt.length ? dest.__preSearchFilters.reduce((prev, next) => next(prev), router.location.search) : router.location.search; // Then the link/navigate function
2393
-
2394
- const destSearch = dest.search === true ? preFilteredSearch // Preserve resolvedFrom true
2395
- : dest.search ? (_functionalUpdate = functionalUpdate(dest.search, preFilteredSearch)) != null ? _functionalUpdate : {} // Updater
2396
- : (_dest$__preSearchFilt2 = dest.__preSearchFilters) != null && _dest$__preSearchFilt2.length ? preFilteredSearch // Preserve resolvedFrom filters
2397
- : {}; // Then post filters
2398
-
2399
- const postFilteredSearch = (_dest$__postSearchFil = dest.__postSearchFilters) != null && _dest$__postSearchFil.length ? dest.__postSearchFilters.reduce((prev, next) => next(prev), destSearch) : destSearch;
2400
- const search = replaceEqualDeep(router.location.search, postFilteredSearch);
2401
- const searchStr = router.options.stringifySearch(search);
2402
- let hash = dest.hash === true ? router.location.hash : functionalUpdate(dest.hash, router.location.hash);
2403
- hash = hash ? "#" + hash : '';
2404
- return {
2405
- pathname,
2406
- search,
2407
- searchStr,
2408
- state: router.location.state,
2409
- hash,
2410
- href: "" + pathname + searchStr + hash,
2411
- key: dest.key
2412
- };
2413
- },
2414
- commitLocation: (next, replace) => {
2415
- const id = '' + Date.now() + Math.random();
2416
- if (router.navigateTimeout) clearTimeout(router.navigateTimeout);
2417
- let nextAction = 'replace';
2418
-
2419
- if (!replace) {
2420
- nextAction = 'push';
1756
+ if (foundMask) {
1757
+ foundMask = {
1758
+ ...foundMask,
1759
+ from: interpolatePath(foundMask.from, params)
1760
+ };
1761
+ maskedDest = foundMask;
1762
+ maskedNext = build(maskedDest);
2421
1763
  }
2422
-
2423
- const isSameUrl = router.__.parseLocation(history.location).href === next.href;
2424
-
2425
- if (isSameUrl && !next.key) {
2426
- nextAction = 'replace';
1764
+ }
1765
+ const nextMatches = this.matchRoutes(next.pathname, next.search);
1766
+ const maskedMatches = maskedNext ? this.matchRoutes(maskedNext.pathname, maskedNext.search) : undefined;
1767
+ const maskedFinal = maskedNext ? build(maskedDest, maskedMatches) : undefined;
1768
+ const final = build(dest, nextMatches);
1769
+ if (maskedFinal) {
1770
+ final.maskedLocation = maskedFinal;
1771
+ }
1772
+ return final;
1773
+ };
1774
+ if (opts.mask) {
1775
+ return buildWithMatches(opts, {
1776
+ ...pick(opts, ['from']),
1777
+ ...opts.mask
1778
+ });
1779
+ }
1780
+ return buildWithMatches(opts);
1781
+ };
1782
+ #buildAndCommitLocation = ({
1783
+ replace,
1784
+ resetScroll,
1785
+ ...rest
1786
+ } = {}) => {
1787
+ const location = this.buildLocation(rest);
1788
+ return this.#commitLocation({
1789
+ ...location,
1790
+ replace,
1791
+ resetScroll
1792
+ });
1793
+ };
1794
+ #commitLocation = async next => {
1795
+ if (this.navigateTimeout) clearTimeout(this.navigateTimeout);
1796
+ let nextAction = 'replace';
1797
+ if (!next.replace) {
1798
+ nextAction = 'push';
1799
+ }
1800
+ const isSameUrl = this.state.location.href === next.href;
1801
+ if (isSameUrl) {
1802
+ nextAction = 'replace';
1803
+ }
1804
+ let {
1805
+ maskedLocation,
1806
+ ...nextHistory
1807
+ } = next;
1808
+ if (maskedLocation) {
1809
+ nextHistory = {
1810
+ ...maskedLocation,
1811
+ state: {
1812
+ ...maskedLocation.state,
1813
+ __tempKey: undefined,
1814
+ __tempLocation: {
1815
+ ...nextHistory,
1816
+ search: nextHistory.searchStr,
1817
+ state: {
1818
+ ...nextHistory.state,
1819
+ __tempKey: undefined,
1820
+ __tempLocation: undefined,
1821
+ key: undefined
1822
+ }
1823
+ }
2427
1824
  }
2428
-
2429
- if (nextAction === 'replace') {
2430
- history.replace({
2431
- pathname: next.pathname,
2432
- hash: next.hash,
2433
- search: next.searchStr
2434
- }, _extends({
2435
- id
2436
- }, next.state));
2437
- } else {
2438
- history.push({
2439
- pathname: next.pathname,
2440
- hash: next.hash,
2441
- search: next.searchStr
2442
- }, {
2443
- id
2444
- });
1825
+ };
1826
+ if (nextHistory.unmaskOnReload ?? this.options.unmaskOnReload ?? false) {
1827
+ nextHistory.state.__tempKey = this.tempLocationKey;
1828
+ }
1829
+ }
1830
+ this.history[nextAction === 'push' ? 'push' : 'replace'](nextHistory.href, nextHistory.state);
1831
+ this.resetNextScroll = next.resetScroll ?? true;
1832
+ return this.latestLoadPromise;
1833
+ };
1834
+ getRouteMatch = id => {
1835
+ return this.state.matchesById[id];
1836
+ };
1837
+ setRouteMatch = (id, updater) => {
1838
+ this.__store.setState(prev => {
1839
+ if (!prev.matchesById[id]) {
1840
+ return prev;
1841
+ }
1842
+ return {
1843
+ ...prev,
1844
+ matchesById: {
1845
+ ...prev.matchesById,
1846
+ [id]: updater(prev.matchesById[id])
2445
1847
  }
2446
-
2447
- router.navigationPromise = new Promise(resolve => {
2448
- const previousNavigationResolve = router.resolveNavigation;
2449
-
2450
- router.resolveNavigation = () => {
2451
- previousNavigationResolve();
2452
- resolve();
2453
- };
1848
+ };
1849
+ });
1850
+ };
1851
+ setRouteMatchData = (id, updater, opts) => {
1852
+ const match = this.getRouteMatch(id);
1853
+ if (!match) return;
1854
+ const route = this.getRoute(match.routeId);
1855
+ const updatedAt = opts?.updatedAt ?? Date.now();
1856
+ const preloadMaxAge = opts?.maxAge ?? route.options.preloadMaxAge ?? this.options.defaultPreloadMaxAge ?? 5000;
1857
+ const maxAge = opts?.maxAge ?? route.options.maxAge ?? this.options.defaultMaxAge ?? -1;
1858
+ this.setRouteMatch(id, s => ({
1859
+ ...s,
1860
+ error: undefined,
1861
+ status: 'success',
1862
+ isFetching: false,
1863
+ updatedAt: updatedAt,
1864
+ loaderData: functionalUpdate(updater, s.loaderData),
1865
+ preloadMaxAge,
1866
+ maxAge
1867
+ }));
1868
+ };
1869
+ invalidate = async opts => {
1870
+ if (opts?.matchId) {
1871
+ this.setRouteMatch(opts.matchId, s => ({
1872
+ ...s,
1873
+ invalid: true
1874
+ }));
1875
+ const matchIndex = this.state.matches.findIndex(d => d.id === opts.matchId);
1876
+ const childMatch = this.state.matches[matchIndex + 1];
1877
+ if (childMatch) {
1878
+ return this.invalidate({
1879
+ matchId: childMatch.id,
1880
+ reload: false,
1881
+ __fromFocus: opts.__fromFocus
2454
1882
  });
2455
- return router.navigationPromise;
2456
1883
  }
1884
+ } else {
1885
+ this.__store.batch(() => {
1886
+ Object.values(this.state.matchesById).forEach(match => {
1887
+ const route = this.getRoute(match.routeId);
1888
+ const shouldInvalidate = opts?.__fromFocus ? route.options.reloadOnWindowFocus ?? true : true;
1889
+ if (shouldInvalidate) {
1890
+ this.setRouteMatch(match.id, s => ({
1891
+ ...s,
1892
+ invalid: true
1893
+ }));
1894
+ }
1895
+ });
1896
+ });
1897
+ }
1898
+ if (opts?.reload ?? true) {
1899
+ return this.load();
2457
1900
  }
2458
1901
  };
2459
- router.update(userOptions); // Allow frameworks to hook into the router creation
2460
-
2461
- router.options.createRouter == null ? void 0 : router.options.createRouter(router);
2462
- return router;
2463
1902
  }
2464
1903
 
1904
+ // Detect if we're in the DOM
1905
+ const isServer = typeof window === 'undefined' || !window.document.createElement;
1906
+ function getInitialRouterState() {
1907
+ return {
1908
+ status: 'idle',
1909
+ isFetching: false,
1910
+ resolvedLocation: null,
1911
+ location: null,
1912
+ matchesById: {},
1913
+ matchIds: [],
1914
+ pendingMatchIds: [],
1915
+ matches: [],
1916
+ pendingMatches: [],
1917
+ renderedMatchIds: [],
1918
+ renderedMatches: [],
1919
+ lastUpdated: Date.now()
1920
+ };
1921
+ }
2465
1922
  function isCtrlEvent(e) {
2466
1923
  return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
2467
1924
  }
1925
+ function redirect(opts) {
1926
+ opts.isRedirect = true;
1927
+ return opts;
1928
+ }
1929
+ function isRedirect(obj) {
1930
+ return !!obj?.isRedirect;
1931
+ }
1932
+ class SearchParamError extends Error {}
1933
+ class PathParamError extends Error {}
1934
+ function escapeJSON(jsonString) {
1935
+ return jsonString.replace(/\\/g, '\\\\') // Escape backslashes
1936
+ .replace(/'/g, "\\'") // Escape single quotes
1937
+ .replace(/"/g, '\\"'); // Escape double quotes
1938
+ }
2468
1939
 
2469
- function cascadeLoaderData(matches) {
2470
- matches.forEach((match, index) => {
2471
- const parent = matches[index - 1];
1940
+ // A function that takes an import() argument which is a function and returns a new function that will
1941
+ // proxy arguments from the caller to the imported function, retaining all type
1942
+ // information along the way
1943
+ function lazyFn(fn, key) {
1944
+ return async (...args) => {
1945
+ const imported = await fn();
1946
+ return imported[key || 'default'](...args);
1947
+ };
1948
+ }
1949
+ function isMatchInvalid(match, opts) {
1950
+ const now = Date.now();
1951
+ if (match.invalid) {
1952
+ return true;
1953
+ }
1954
+ if (opts?.preload) {
1955
+ return match.preloadMaxAge < 0 ? false : match.updatedAt + match.preloadMaxAge < now;
1956
+ }
1957
+ return match.maxAge < 0 ? false : match.updatedAt + match.maxAge < now;
1958
+ }
2472
1959
 
2473
- if (parent) {
2474
- match.loaderData = replaceEqualDeep(match.loaderData, _extends({}, parent.loaderData, match.routeLoaderData));
1960
+ const windowKey = 'window';
1961
+ const delimiter = '___';
1962
+ let weakScrolledElementsByRestoreKey = {};
1963
+ let cache;
1964
+ let pathDidChange = false;
1965
+ const sessionsStorage = typeof window !== 'undefined' && window.sessionStorage;
1966
+ const defaultGetKey = location => location.state.key;
1967
+ function watchScrollPositions(router, opts) {
1968
+ const getKey = opts?.getKey || defaultGetKey;
1969
+ if (sessionsStorage) {
1970
+ if (!cache) {
1971
+ cache = (() => {
1972
+ const storageKey = 'tsr-scroll-restoration-v1';
1973
+ const current = JSON.parse(window.sessionStorage.getItem(storageKey) || '{}');
1974
+ return {
1975
+ current,
1976
+ set: (key, value) => {
1977
+ current[key] = value;
1978
+ window.sessionStorage.setItem(storageKey, JSON.stringify(cache));
1979
+ }
1980
+ };
1981
+ })();
2475
1982
  }
1983
+ }
1984
+ const {
1985
+ history
1986
+ } = window;
1987
+ if (history.scrollRestoration) {
1988
+ history.scrollRestoration = 'manual';
1989
+ }
1990
+ const onScroll = event => {
1991
+ const restoreKey = getKey(router.state.resolvedLocation);
1992
+ if (!weakScrolledElementsByRestoreKey[restoreKey]) {
1993
+ weakScrolledElementsByRestoreKey[restoreKey] = new WeakSet();
1994
+ }
1995
+ const set = weakScrolledElementsByRestoreKey[restoreKey];
1996
+ if (set.has(event.target)) return;
1997
+ set.add(event.target);
1998
+ const cacheKey = [restoreKey, event.target === document || event.target === window ? windowKey : getCssSelector(event.target)].join(delimiter);
1999
+ if (!cache.current[cacheKey]) {
2000
+ cache.set(cacheKey, {
2001
+ scrollX: NaN,
2002
+ scrollY: NaN
2003
+ });
2004
+ }
2005
+ };
2006
+ const getCssSelector = el => {
2007
+ let path = [],
2008
+ parent;
2009
+ while (parent = el.parentNode) {
2010
+ path.unshift(`${el.tagName}:nth-child(${[].indexOf.call(parent.children, el) + 1})`);
2011
+ el = parent;
2012
+ }
2013
+ return `${path.join(' > ')}`.toLowerCase();
2014
+ };
2015
+ const onPathWillChange = from => {
2016
+ const restoreKey = getKey(from);
2017
+ for (const cacheKey in cache.current) {
2018
+ const entry = cache.current[cacheKey];
2019
+ const [key, elementSelector] = cacheKey.split(delimiter);
2020
+ if (restoreKey === key) {
2021
+ if (elementSelector === windowKey) {
2022
+ entry.scrollX = window.scrollX || 0;
2023
+ entry.scrollY = window.scrollY || 0;
2024
+ } else if (elementSelector) {
2025
+ const element = document.querySelector(elementSelector);
2026
+ entry.scrollX = element?.scrollLeft || 0;
2027
+ entry.scrollY = element?.scrollTop || 0;
2028
+ }
2029
+ cache.set(cacheKey, entry);
2030
+ }
2031
+ }
2032
+ };
2033
+ const onPathChange = () => {
2034
+ pathDidChange = true;
2035
+ };
2036
+ if (typeof document !== 'undefined') {
2037
+ document.addEventListener('scroll', onScroll, true);
2038
+ }
2039
+ const unsubOnBeforeLoad = router.subscribe('onBeforeLoad', event => {
2040
+ if (event.pathChanged) onPathWillChange(event.from);
2041
+ });
2042
+ const unsubOnLoad = router.subscribe('onLoad', event => {
2043
+ if (event.pathChanged) onPathChange();
2476
2044
  });
2045
+ return () => {
2046
+ document.removeEventListener('scroll', onScroll);
2047
+ unsubOnBeforeLoad();
2048
+ unsubOnLoad();
2049
+ };
2050
+ }
2051
+ function restoreScrollPositions(router, opts) {
2052
+ if (pathDidChange) {
2053
+ if (!router.resetNextScroll) {
2054
+ return;
2055
+ }
2056
+ const getKey = opts?.getKey || defaultGetKey;
2057
+ pathDidChange = false;
2058
+ const restoreKey = getKey(router.state.location);
2059
+ let windowRestored = false;
2060
+ for (const cacheKey in cache.current) {
2061
+ const entry = cache.current[cacheKey];
2062
+ const [key, elementSelector] = cacheKey.split(delimiter);
2063
+ if (key === restoreKey) {
2064
+ if (elementSelector === windowKey) {
2065
+ windowRestored = true;
2066
+ window.scrollTo(entry.scrollX, entry.scrollY);
2067
+ } else if (elementSelector) {
2068
+ const element = document.querySelector(elementSelector);
2069
+ if (element) {
2070
+ element.scrollLeft = entry.scrollX;
2071
+ element.scrollTop = entry.scrollY;
2072
+ }
2073
+ }
2074
+ }
2075
+ }
2076
+ if (!windowRestored) {
2077
+ window.scrollTo(0, 0);
2078
+ }
2079
+ }
2080
+ }
2081
+
2082
+ function defer(_promise) {
2083
+ const promise = _promise;
2084
+ if (!promise.__deferredState) {
2085
+ promise.__deferredState = {
2086
+ uid: Math.random().toString(36).slice(2),
2087
+ status: 'pending'
2088
+ };
2089
+ const state = promise.__deferredState;
2090
+ promise.then(data => {
2091
+ state.status = 'success';
2092
+ state.data = data;
2093
+ }).catch(error => {
2094
+ state.status = 'error';
2095
+ state.error = error;
2096
+ });
2097
+ }
2098
+ return promise;
2099
+ }
2100
+ function isDehydratedDeferred(obj) {
2101
+ return typeof obj === 'object' && obj !== null && !(obj instanceof Promise) && !obj.then && '__deferredState' in obj;
2477
2102
  }
2478
2103
 
2479
- export { cleanPath, createBrowserHistory, createHashHistory, createMemoryHistory, createRoute, createRouteConfig, createRouteMatch, createRouter, decode, defaultParseSearch, defaultStringifySearch, encode, functionalUpdate, interpolatePath, invariant, joinPaths, last, matchByPath, matchPathname, parsePathname, parseSearchWith, pick, replaceEqualDeep, resolvePath, rootRouteId, stringifySearchWith, trimPath, trimPathLeft, trimPathRight, warning };
2104
+ export { FileRoute, PathParamError, RootRoute, Route, Router, RouterContext, SearchParamError, cleanPath, componentTypes, createBrowserHistory, createHashHistory, createMemoryHistory, createRouteMask, decode, defaultParseSearch, defaultStringifySearch, defer, encode, functionalUpdate, interpolatePath, isDehydratedDeferred, isMatchInvalid, isPlainObject, isRedirect, joinPaths, last, lazyFn, matchByPath, matchPathname, parsePathname, parseSearchWith, partialDeepEqual, pick, redirect, replaceEqualDeep, resolvePath, restoreScrollPositions, rootRouteId, stringifySearchWith, trimPath, trimPathLeft, trimPathRight, watchScrollPositions };
2480
2105
  //# sourceMappingURL=index.js.map