@tanstack/react-router 0.0.1-beta.23 → 0.0.1-beta.231

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