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

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 +232 -0
  5. package/build/cjs/Matches.js.map +1 -0
  6. package/build/cjs/RouterProvider.js +158 -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 +122 -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 +1099 -0
  31. package/build/cjs/router.js.map +1 -0
  32. package/build/cjs/scroll-restoration.js +179 -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 +2149 -2560
  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 +6 -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 +2443 -2515
  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 +387 -0
  82. package/src/RouterProvider.tsx +224 -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 +1660 -0
  98. package/src/scroll-restoration.tsx +192 -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,2046 @@ 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 locationKey = useRouterState().location.state?.key;
654
+ const PendingComponent = route.options.pendingComponent ?? options.defaultPendingComponent;
655
+ const pendingElement = PendingComponent ? /*#__PURE__*/React.createElement(PendingComponent, {
656
+ useMatch: route.useMatch,
657
+ useRouteContext: route.useRouteContext,
658
+ useSearch: route.useSearch,
659
+ useParams: route.useParams
660
+ }) : undefined;
661
+ const routeErrorComponent = route.options.errorComponent ?? options.defaultErrorComponent ?? ErrorComponent;
662
+ const ResolvedSuspenseBoundary = route.options.wrapInSuspense ?? pendingElement ? React.Suspense : SafeFragment;
663
+ const errorComponent = routeErrorComponent ? React.useCallback(props => {
664
+ return /*#__PURE__*/React.createElement(routeErrorComponent, {
665
+ ...props,
666
+ useMatch: route.useMatch,
667
+ useRouteContext: route.useRouteContext,
668
+ useSearch: route.useSearch,
669
+ useParams: route.useParams
670
+ });
671
+ }, [route]) : undefined;
672
+ const ResolvedCatchBoundary = errorComponent ? CatchBoundary : SafeFragment;
673
+ return /*#__PURE__*/React.createElement(matchesContext.Provider, {
674
+ value: matches
675
+ }, /*#__PURE__*/React.createElement(ResolvedSuspenseBoundary, {
676
+ fallback: pendingElement
677
+ }, /*#__PURE__*/React.createElement(ResolvedCatchBoundary, {
678
+ resetKey: locationKey,
679
+ errorComponent: errorComponent,
680
+ onCatch: () => {
681
+ warning(false, `Error in route match: ${match.id}`);
1362
682
  }
1363
- };
1364
- router.options.createRoute == null ? void 0 : router.options.createRoute({
1365
- router,
1366
- route
1367
- });
1368
- return route;
683
+ }, /*#__PURE__*/React.createElement(MatchInner, {
684
+ match: match,
685
+ pendingElement: pendingElement
686
+ }))));
1369
687
  }
1370
-
1371
- const rootRouteId = '__root__';
1372
- const createRouteConfig = function createRouteConfig(options, children, isRoot, parentId, parentPath) {
1373
- if (options === void 0) {
1374
- options = {};
688
+ function MatchInner({
689
+ match,
690
+ pendingElement
691
+ }) {
692
+ const {
693
+ options,
694
+ routesById
695
+ } = useRouter();
696
+ const route = routesById[match.routeId];
697
+ if (match.status === 'error') {
698
+ throw match.error;
1375
699
  }
1376
-
1377
- if (isRoot === void 0) {
1378
- isRoot = true;
700
+ if (match.status === 'pending') {
701
+ if (match.showPending) {
702
+ return pendingElement || null;
703
+ }
704
+ throw match.loadPromise;
1379
705
  }
1380
-
1381
- if (isRoot) {
1382
- options.path = rootRouteId;
1383
- } // Strip the root from parentIds
1384
-
1385
-
1386
- if (parentId === rootRouteId) {
1387
- parentId = '';
706
+ if (match.status === 'success') {
707
+ let comp = route.options.component ?? options.defaultComponent;
708
+ if (comp) {
709
+ return /*#__PURE__*/React.createElement(comp, {
710
+ useMatch: route.useMatch,
711
+ useRouteContext: route.useRouteContext,
712
+ useSearch: route.useSearch,
713
+ useParams: route.useParams,
714
+ useLoaderData: route.useLoaderData
715
+ });
716
+ }
717
+ return /*#__PURE__*/React.createElement(Outlet, null);
1388
718
  }
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);
719
+ invariant(false, 'Idle routeMatch status encountered during rendering! You should never see this. File an issue!');
720
+ }
721
+ function Outlet() {
722
+ const matches = React.useContext(matchesContext).slice(1);
723
+ if (!matches[0]) {
724
+ return null;
1394
725
  }
1395
-
1396
- const routeId = path || options.id;
1397
- let id = joinPaths([parentId, routeId]);
1398
-
1399
- if (path === rootRouteId) {
1400
- path = '/';
726
+ return /*#__PURE__*/React.createElement(Match, {
727
+ matches: matches
728
+ });
729
+ }
730
+ function useMatchRoute() {
731
+ const {
732
+ matchRoute
733
+ } = useRouter();
734
+ return React.useCallback(opts => {
735
+ const {
736
+ pending,
737
+ caseSensitive,
738
+ ...rest
739
+ } = opts;
740
+ return matchRoute(rest, {
741
+ pending,
742
+ caseSensitive
743
+ });
744
+ }, []);
745
+ }
746
+ function MatchRoute(props) {
747
+ const matchRoute = useMatchRoute();
748
+ const params = matchRoute(props);
749
+ if (typeof props.children === 'function') {
750
+ return props.children(params);
1401
751
  }
1402
-
1403
- if (id !== rootRouteId) {
1404
- id = joinPaths(['/', id]);
752
+ return !!params ? props.children : null;
753
+ }
754
+ function useMatch(opts) {
755
+ const nearestMatch = React.useContext(matchesContext)[0];
756
+ const nearestMatchRouteId = nearestMatch?.routeId;
757
+ const matchRouteId = useRouterState({
758
+ select: state => {
759
+ const match = opts?.from ? state.matches.find(d => d.routeId === opts?.from) : state.matches.find(d => d.id === nearestMatch.id);
760
+ return match.routeId;
761
+ }
762
+ });
763
+ if (opts?.strict ?? true) {
764
+ 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
765
  }
766
+ const matchSelection = useRouterState({
767
+ select: state => {
768
+ const match = opts?.from ? state.matches.find(d => d.routeId === opts?.from) : state.matches.find(d => d.id === nearestMatch.id);
769
+ invariant(match, `Could not find ${opts?.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`);
770
+ return opts?.select ? opts.select(match) : match;
771
+ }
772
+ });
773
+ return matchSelection;
774
+ }
775
+ const matchesContext = /*#__PURE__*/React.createContext(null);
776
+ function useMatches(opts) {
777
+ const contextMatches = React.useContext(matchesContext);
778
+ return useRouterState({
779
+ select: state => {
780
+ const matches = state.matches.slice(state.matches.findIndex(d => d.id === contextMatches[0]?.id));
781
+ return opts?.select ? opts.select(matches) : matches;
782
+ }
783
+ });
784
+ }
785
+ function useLoaderData(opts) {
786
+ const match = useMatch({
787
+ ...opts,
788
+ select: undefined
789
+ });
790
+ return typeof opts.select === 'function' ? opts.select(match?.loaderData) : match?.loaderData;
791
+ }
1406
792
 
1407
- const fullPath = id === rootRouteId ? '/' : trimPathRight(joinPaths([parentPath, path]));
793
+ const routerContext = /*#__PURE__*/React.createContext(null);
794
+ if (typeof document !== 'undefined') {
795
+ window.__TSR_ROUTER_CONTEXT__ = routerContext;
796
+ }
797
+ class SearchParamError extends Error {}
798
+ class PathParamError extends Error {}
799
+ function getInitialRouterState(location) {
1408
800
  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
801
  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 () => {
802
+ resolvedLocation: location,
803
+ location,
804
+ matches: [],
805
+ pendingMatches: [],
806
+ lastUpdated: Date.now()
807
+ };
808
+ }
809
+ function RouterProvider({
810
+ router,
811
+ ...rest
812
+ }) {
813
+ // Allow the router to update options on the router instance
814
+ router.updateOptions({
815
+ ...router.options,
816
+ ...rest,
817
+ context: {
818
+ ...router.options.context,
819
+ ...rest?.context
820
+ }
821
+ });
822
+ const [preState, setState] = React.useState(() => router.state);
823
+ const [isTransitioning, startReactTransition] = React.useTransition();
824
+ const state = React.useMemo(() => ({
825
+ ...preState,
826
+ status: isTransitioning ? 'pending' : 'idle',
827
+ location: isTransitioning ? router.latestLocation : preState.location,
828
+ pendingMatches: router.pendingMatches
829
+ }), [preState, isTransitioning]);
830
+ router.setState = setState;
831
+ router.state = state;
832
+ router.startReactTransition = startReactTransition;
833
+ React.useLayoutEffect(() => {
834
+ const unsub = router.history.subscribe(() => {
835
+ router.latestLocation = router.parseLocation(router.latestLocation);
836
+ if (state.location !== router.latestLocation) {
837
+ startReactTransition(() => {
1567
838
  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;
839
+ router.load();
1581
840
  } 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;
841
+ console.error(err);
1592
842
  }
1593
843
  });
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
- }
844
+ }
845
+ });
846
+ const nextLocation = router.buildLocation({
847
+ search: true,
848
+ params: true,
849
+ hash: true,
850
+ state: true
851
+ });
852
+ if (state.location.href !== nextLocation.href) {
853
+ router.commitLocation({
854
+ ...nextLocation,
855
+ replace: true
856
+ });
857
+ }
858
+ return () => {
859
+ unsub();
860
+ };
861
+ }, [router.history]);
862
+ React.useLayoutEffect(() => {
863
+ if (!isTransitioning && state.resolvedLocation !== state.location) {
864
+ router.emit({
865
+ type: 'onResolved',
866
+ fromLocation: state.resolvedLocation,
867
+ toLocation: state.location,
868
+ pathChanged: state.location.href !== state.resolvedLocation?.href
1609
869
  });
1610
- await routeMatch.__.loadPromise;
1611
- await checkLatest();
870
+ router.pendingMatches = [];
871
+ setState(s => ({
872
+ ...s,
873
+ resolvedLocation: s.location
874
+ }));
1612
875
  }
1613
876
  });
877
+ React.useLayoutEffect(() => {
878
+ startReactTransition(() => {
879
+ try {
880
+ router.load();
881
+ } catch (err) {
882
+ console.error(err);
883
+ }
884
+ });
885
+ }, []);
886
+ return /*#__PURE__*/React.createElement(routerContext.Provider, {
887
+ value: router
888
+ }, /*#__PURE__*/React.createElement(Matches, null));
889
+ }
890
+ function getRouteMatch(state, id) {
891
+ return [...state.pendingMatches, ...state.matches].find(d => d.id === id);
892
+ }
893
+ function useRouterState(opts) {
894
+ const {
895
+ state
896
+ } = useRouter();
897
+ // return useStore(router.__store, opts?.select as any)
898
+ return opts?.select ? opts.select(state) : state;
899
+ }
900
+ function useRouter() {
901
+ const resolvedContext = window.__TSR_ROUTER_CONTEXT__ || routerContext;
902
+ const value = React.useContext(resolvedContext);
903
+ warning(value, 'useRouter must be used inside a <RouterProvider> component!');
904
+ return value;
905
+ }
1614
906
 
1615
- if (!routeMatch.hasLoaders()) {
1616
- routeMatch.status = 'success';
907
+ function defer(_promise) {
908
+ const promise = _promise;
909
+ if (!promise.__deferredState) {
910
+ promise.__deferredState = {
911
+ uid: Math.random().toString(36).slice(2),
912
+ status: 'pending'
913
+ };
914
+ const state = promise.__deferredState;
915
+ promise.then(data => {
916
+ state.status = 'success';
917
+ state.data = data;
918
+ }).catch(error => {
919
+ state.status = 'error';
920
+ state.error = error;
921
+ });
1617
922
  }
1618
-
1619
- return routeMatch;
923
+ return promise;
924
+ }
925
+ function isDehydratedDeferred(obj) {
926
+ return typeof obj === 'object' && obj !== null && !(obj instanceof Promise) && !obj.then && '__deferredState' in obj;
1620
927
  }
1621
928
 
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
929
+ function useAwaited({
930
+ promise
931
+ }) {
932
+ const router = useRouter();
933
+ let state = promise.__deferredState;
934
+ const key = `__TSR__DEFERRED__${state.uid}`;
935
+ if (isDehydratedDeferred(promise)) {
936
+ state = router.hydrateData(key);
937
+ promise = Promise.resolve(state.data);
938
+ promise.__deferredState = state;
939
+ }
940
+ if (state.status === 'pending') {
941
+ throw promise;
942
+ }
943
+ if (state.status === 'error') {
944
+ throw state.error;
945
+ }
946
+ router.dehydrateData(key, state);
947
+ return [state.data];
948
+ }
949
+ function Await(props) {
950
+ const awaited = useAwaited(props);
951
+ return props.children(...awaited);
952
+ }
1631
953
 
1632
- for (let key in query) {
1633
- const value = query[key];
954
+ class FileRoute {
955
+ constructor(path) {
956
+ this.path = path;
957
+ }
958
+ createRoute = options => {
959
+ const route = new Route(options);
960
+ route.isRoot = false;
961
+ return route;
962
+ };
963
+ }
1634
964
 
1635
- if (typeof value === 'string') {
1636
- try {
1637
- query[key] = parser(value);
1638
- } catch (err) {//
1639
- }
1640
- }
965
+ function lazyRouteComponent(importer, exportName) {
966
+ let loadPromise;
967
+ const load = () => {
968
+ if (!loadPromise) {
969
+ loadPromise = importer();
1641
970
  }
1642
-
1643
- return query;
971
+ return loadPromise;
1644
972
  };
973
+ const lazyComp = /*#__PURE__*/React.lazy(async () => {
974
+ const moduleExports = await load();
975
+ const comp = moduleExports[exportName ?? 'default'];
976
+ return {
977
+ default: comp
978
+ };
979
+ });
980
+ lazyComp.preload = load;
981
+ return lazyComp;
1645
982
  }
1646
- function stringifySearchWith(stringify) {
1647
- return search => {
1648
- search = _extends({}, search);
1649
983
 
1650
- if (search) {
1651
- Object.keys(search).forEach(key => {
1652
- const val = search[key];
1653
-
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
- }
984
+ function _extends() {
985
+ _extends = Object.assign ? Object.assign.bind() : function (target) {
986
+ for (var i = 1; i < arguments.length; i++) {
987
+ var source = arguments[i];
988
+ for (var key in source) {
989
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
990
+ target[key] = source[key];
1661
991
  }
1662
- });
992
+ }
1663
993
  }
1664
-
1665
- const searchStr = encode(search).toString();
1666
- return searchStr ? "?" + searchStr : '';
994
+ return target;
1667
995
  };
996
+ return _extends.apply(this, arguments);
1668
997
  }
1669
998
 
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
999
+ function useLinkProps(options) {
1000
+ const {
1001
+ buildLink
1002
+ } = useRouter();
1003
+ const match = useMatch({
1004
+ strict: false
1005
+ });
1006
+ const {
1007
+ // custom props
1008
+ type,
1009
+ children,
1010
+ target,
1011
+ activeProps = () => ({
1012
+ className: 'active'
1013
+ }),
1014
+ inactiveProps = () => ({}),
1015
+ activeOptions,
1016
+ disabled,
1017
+ hash,
1018
+ search,
1019
+ params,
1020
+ to,
1021
+ state,
1022
+ mask,
1023
+ preload,
1024
+ preloadDelay,
1025
+ replace,
1026
+ startTransition,
1027
+ resetScroll,
1028
+ // element props
1029
+ style,
1030
+ className,
1031
+ onClick,
1032
+ onFocus,
1033
+ onMouseEnter,
1034
+ onMouseLeave,
1035
+ onTouchStart,
1036
+ ...rest
1037
+ } = options;
1038
+ const linkInfo = buildLink({
1039
+ from: options.to ? match.pathname : undefined,
1040
+ ...options
1041
+ });
1042
+ if (linkInfo.type === 'external') {
1043
+ const {
1044
+ href
1045
+ } = linkInfo;
1046
+ return {
1047
+ href
1048
+ };
1049
+ }
1050
+ const {
1051
+ handleClick,
1052
+ handleFocus,
1053
+ handleEnter,
1054
+ handleLeave,
1055
+ handleTouchStart,
1056
+ isActive,
1057
+ next
1058
+ } = linkInfo;
1059
+ const composeHandlers = handlers => e => {
1060
+ if (e.persist) e.persist();
1061
+ handlers.filter(Boolean).forEach(handler => {
1062
+ if (e.defaultPrevented) return;
1063
+ handler(e);
1064
+ });
1065
+ };
1673
1066
 
1674
- const createDefaultHistory = () => isServer ? createMemoryHistory() : createBrowserHistory();
1067
+ // Get the active props
1068
+ const resolvedActiveProps = isActive ? functionalUpdate(activeProps, {}) ?? {} : {};
1675
1069
 
1676
- function getInitialRouterState() {
1070
+ // Get the inactive props
1071
+ const resolvedInactiveProps = isActive ? {} : functionalUpdate(inactiveProps, {}) ?? {};
1677
1072
  return {
1678
- status: 'idle',
1679
- location: null,
1680
- matches: [],
1681
- actions: {},
1682
- loaders: {},
1683
- lastUpdated: Date.now(),
1684
- isFetching: false,
1685
- isPreloading: false
1073
+ ...resolvedActiveProps,
1074
+ ...resolvedInactiveProps,
1075
+ ...rest,
1076
+ href: disabled ? undefined : next.maskedLocation ? next.maskedLocation.href : next.href,
1077
+ onClick: composeHandlers([onClick, handleClick]),
1078
+ onFocus: composeHandlers([onFocus, handleFocus]),
1079
+ onMouseEnter: composeHandlers([onMouseEnter, handleEnter]),
1080
+ onMouseLeave: composeHandlers([onMouseLeave, handleLeave]),
1081
+ onTouchStart: composeHandlers([onTouchStart, handleTouchStart]),
1082
+ target,
1083
+ style: {
1084
+ ...style,
1085
+ ...resolvedActiveProps.style,
1086
+ ...resolvedInactiveProps.style
1087
+ },
1088
+ className: [className, resolvedActiveProps.className, resolvedInactiveProps.className].filter(Boolean).join(' ') || undefined,
1089
+ ...(disabled ? {
1090
+ role: 'link',
1091
+ 'aria-disabled': true
1092
+ } : undefined),
1093
+ ['data-status']: isActive ? 'active' : undefined
1686
1094
  };
1687
1095
  }
1096
+ const Link = /*#__PURE__*/React.forwardRef((props, ref) => {
1097
+ const linkProps = useLinkProps(props);
1098
+ return /*#__PURE__*/React.createElement("a", _extends({
1099
+ ref: ref
1100
+ }, linkProps, {
1101
+ children: typeof props.children === 'function' ? props.children({
1102
+ isActive: linkProps['data-status'] === 'active'
1103
+ }) : props.children
1104
+ }));
1105
+ });
1688
1106
 
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
-
1777
-
1778
- if (next.href !== router.location.href) {
1779
- router.__.commitLocation(next, true);
1780
- }
1781
-
1782
- if (!router.state.matches.length) {
1783
- router.load();
1784
- }
1785
-
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
- }
1796
-
1797
- return () => {
1798
- unsub();
1107
+ // @ts-nocheck
1799
1108
 
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;
1109
+ // 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.
1812
1110
 
1813
- if (!router.location || newHistory) {
1814
- if (opts != null && opts.history) {
1815
- router.history = opts.history;
1111
+ function encode(obj, pfx) {
1112
+ var k,
1113
+ i,
1114
+ tmp,
1115
+ str = '';
1116
+ for (k in obj) {
1117
+ if ((tmp = obj[k]) !== void 0) {
1118
+ if (Array.isArray(tmp)) {
1119
+ for (i = 0; i < tmp.length; i++) {
1120
+ str && (str += '&');
1121
+ str += encodeURIComponent(k) + '=' + encodeURIComponent(tmp[i]);
1816
1122
  }
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);
1123
+ } else {
1124
+ str && (str += '&');
1125
+ str += encodeURIComponent(k) + '=' + encodeURIComponent(tmp);
1832
1126
  }
1127
+ }
1128
+ }
1129
+ return (pfx || '') + str;
1130
+ }
1131
+ function toValue(mix) {
1132
+ if (!mix) return '';
1133
+ var str = decodeURIComponent(mix);
1134
+ if (str === 'false') return false;
1135
+ if (str === 'true') return true;
1136
+ return +str * 0 === 0 && +str + '' === str ? +str : str;
1137
+ }
1138
+ function decode(str) {
1139
+ var tmp,
1140
+ k,
1141
+ out = {},
1142
+ arr = str.split('&');
1143
+ while (tmp = arr.shift()) {
1144
+ tmp = tmp.split('=');
1145
+ k = tmp.shift();
1146
+ if (out[k] !== void 0) {
1147
+ out[k] = [].concat(out[k], toValue(tmp.shift()));
1148
+ } else {
1149
+ out[k] = toValue(tmp.shift());
1150
+ }
1151
+ }
1152
+ return out;
1153
+ }
1833
1154
 
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
-
1155
+ // Detect if we're in the DOM
1851
1156
 
1852
- router.cancelMatches(); // Match the routes
1157
+ function redirect(opts) {
1158
+ opts.isRedirect = true;
1159
+ return opts;
1160
+ }
1161
+ function isRedirect(obj) {
1162
+ return !!obj?.isRedirect;
1163
+ }
1853
1164
 
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
1165
+ const defaultParseSearch = parseSearchWith(JSON.parse);
1166
+ const defaultStringifySearch = stringifySearchWith(JSON.stringify, JSON.parse);
1167
+ function parseSearchWith(parser) {
1168
+ return searchStr => {
1169
+ if (searchStr.substring(0, 1) === '?') {
1170
+ searchStr = searchStr.substring(1);
1171
+ }
1172
+ let query = decode(searchStr);
1857
1173
 
1858
- try {
1859
- await Promise.all(matches.map(match => match.options.beforeLoad == null ? void 0 : match.options.beforeLoad({
1860
- context: router.context
1861
- })));
1862
- } catch (err) {
1863
- if (err != null && err.then) {
1864
- await new Promise(() => {});
1174
+ // Try to parse any query params that might be json
1175
+ for (let key in query) {
1176
+ const value = query[key];
1177
+ if (typeof value === 'string') {
1178
+ try {
1179
+ query[key] = parser(value);
1180
+ } catch (err) {
1181
+ //
1865
1182
  }
1866
-
1867
- throw err;
1868
1183
  }
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
- });
1184
+ }
1185
+ return query;
1186
+ };
1187
+ }
1188
+ function stringifySearchWith(stringify, parser) {
1189
+ function stringifyValue(val) {
1190
+ if (typeof val === 'object' && val !== null) {
1191
+ try {
1192
+ return stringify(val);
1193
+ } catch (err) {
1194
+ // silent
1884
1195
  }
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;
1196
+ } else if (typeof val === 'string' && typeof parser === 'function') {
1197
+ try {
1198
+ // Check if it's a valid parseable string.
1199
+ // If it is, then stringify it again.
1200
+ parser(val);
1201
+ return stringify(val);
1202
+ } catch (err) {
1203
+ // silent
1893
1204
  }
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);
1205
+ }
1206
+ return val;
1207
+ }
1208
+ return search => {
1209
+ search = {
1210
+ ...search
1211
+ };
1212
+ if (search) {
1213
+ Object.keys(search).forEach(key => {
1214
+ const val = search[key];
1215
+ if (typeof val === 'undefined' || val === undefined) {
1216
+ delete search[key];
1901
1217
  } else {
1902
- exiting.push(d);
1218
+ search[key] = stringifyValue(val);
1903
1219
  }
1904
1220
  });
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
- }
1221
+ }
1222
+ const searchStr = encode(search).toString();
1223
+ return searchStr ? `?${searchStr}` : '';
1224
+ };
1225
+ }
1921
1226
 
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);
1227
+ //
1923
1228
 
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
1229
+ const componentTypes = ['component', 'errorComponent', 'pendingComponent'];
1230
+ const preloadWarning = 'Error preloading route! ☝️';
1231
+ class Router {
1232
+ // Option-independent properties
1233
+ tempLocationKey = `${Math.round(Math.random() * 10000000)}`;
1234
+ resetNextScroll = true;
1235
+ navigateTimeout = null;
1236
+ latestLoadPromise = Promise.resolve();
1237
+ subscribers = new Set();
1238
+ pendingMatches = [];
1239
+ injectedHtml = [];
1240
+
1241
+ // Must build in constructor
1242
+
1243
+ constructor(options) {
1244
+ this.updateOptions({
1245
+ defaultPreloadDelay: 50,
1246
+ defaultPendingMs: 1000,
1247
+ defaultPendingMinMs: 500,
1248
+ context: undefined,
1249
+ ...options,
1250
+ stringifySearch: options?.stringifySearch ?? defaultStringifySearch,
1251
+ parseSearch: options?.parseSearch ?? defaultParseSearch
1252
+ });
1253
+ }
1254
+ startReactTransition = () => {
1255
+ warning(false, 'startReactTransition implementation is missing. If you see this, please file an issue.');
1256
+ };
1257
+ setState = () => {
1258
+ warning(false, 'setState implementation is missing. If you see this, please file an issue.');
1259
+ };
1260
+ updateOptions = newOptions => {
1261
+ this.options = {
1262
+ ...this.options,
1263
+ ...newOptions
1264
+ };
1265
+ this.basepath = `/${trimPath(newOptions.basepath ?? '') ?? ''}`;
1266
+ if (!this.history || this.options.history && this.options.history !== this.history) {
1267
+ this.history = this.options.history ?? createBrowserHistory();
1268
+ this.latestLocation = this.parseLocation();
1269
+ }
1270
+ if (this.options.routeTree !== this.routeTree) {
1271
+ this.routeTree = this.options.routeTree;
1272
+ this.buildRouteTree();
1273
+ }
1274
+ if (!this.state) {
1275
+ this.state = getInitialRouterState(this.latestLocation);
1276
+ }
1277
+ };
1278
+ buildRouteTree = () => {
1279
+ this.routesById = {};
1280
+ this.routesByPath = {};
1281
+ const recurseRoutes = childRoutes => {
1282
+ childRoutes.forEach((childRoute, i) => {
1283
+ // if (typeof childRoute === 'function') {
1284
+ // childRoute = (childRoute as any)()
1285
+ // }
1286
+ childRoute.init({
1287
+ originalIndex: i
1941
1288
  });
1942
- delete router.matchCache[d.matchId];
1289
+ const existingRoute = this.routesById[childRoute.id];
1290
+ invariant(!existingRoute, `Duplicate routes found with id: ${String(childRoute.id)}`);
1291
+ this.routesById[childRoute.id] = childRoute;
1292
+ if (!childRoute.isRoot && childRoute.path) {
1293
+ const trimmedFullPath = trimPathRight(childRoute.fullPath);
1294
+ if (!this.routesByPath[trimmedFullPath] || childRoute.fullPath.endsWith('/')) {
1295
+ this.routesByPath[trimmedFullPath] = childRoute;
1296
+ }
1297
+ }
1298
+ const children = childRoute.children;
1299
+ if (children?.length) {
1300
+ recurseRoutes(children);
1301
+ }
1943
1302
  });
1944
-
1945
- if (router.startedLoadingAt !== id) {
1946
- // Ignore side-effects of match loading
1947
- return;
1303
+ };
1304
+ recurseRoutes([this.routeTree]);
1305
+ this.flatRoutes = Object.values(this.routesByPath).map((d, i) => {
1306
+ const trimmed = trimPath(d.fullPath);
1307
+ const parsed = parsePathname(trimmed);
1308
+ while (parsed.length > 1 && parsed[0]?.value === '/') {
1309
+ parsed.shift();
1948
1310
  }
1949
-
1950
- matches.forEach(match => {
1951
- // Clear actions
1952
- if (match.action) {
1953
- match.action.current = undefined;
1954
- match.action.submissions = [];
1311
+ const score = parsed.map(d => {
1312
+ if (d.type === 'param') {
1313
+ return 0.5;
1955
1314
  }
1315
+ if (d.type === 'wildcard') {
1316
+ return 0.25;
1317
+ }
1318
+ return 1;
1956
1319
  });
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
-
1320
+ return {
1321
+ child: d,
1322
+ trimmed,
1323
+ parsed,
1324
+ index: i,
1325
+ score
1326
+ };
1327
+ }).sort((a, b) => {
1328
+ let isIndex = a.trimmed === '/' ? 1 : b.trimmed === '/' ? -1 : 0;
1329
+ if (isIndex !== 0) return isIndex;
1330
+ const length = Math.min(a.score.length, b.score.length);
1331
+
1332
+ // Sort by length of score
1333
+ if (a.score.length !== b.score.length) {
1334
+ return b.score.length - a.score.length;
1335
+ }
1980
1336
 
1981
- delete router.matchCache[matchId];
1982
- });
1983
- },
1984
- loadRoute: async function loadRoute(navigateOpts) {
1985
- if (navigateOpts === void 0) {
1986
- navigateOpts = router.location;
1337
+ // Sort by min available score
1338
+ for (let i = 0; i < length; i++) {
1339
+ if (a.score[i] !== b.score[i]) {
1340
+ return b.score[i] - a.score[i];
1341
+ }
1987
1342
  }
1988
1343
 
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;
1344
+ // Sort by min available parsed value
1345
+ for (let i = 0; i < length; i++) {
1346
+ if (a.parsed[i].value !== b.parsed[i].value) {
1347
+ return a.parsed[i].value > b.parsed[i].value ? 1 : -1;
1348
+ }
1349
+ }
1998
1350
 
1999
- if (navigateOpts === void 0) {
2000
- navigateOpts = router.location;
1351
+ // Sort by length of trimmed full path
1352
+ if (a.trimmed !== b.trimmed) {
1353
+ return a.trimmed > b.trimmed ? 1 : -1;
2001
1354
  }
2002
1355
 
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
1356
+ // Sort by original index
1357
+ return a.index - b.index;
1358
+ }).map((d, i) => {
1359
+ d.child.rank = i;
1360
+ return d.child;
1361
+ });
1362
+ };
1363
+ subscribe = (eventType, fn) => {
1364
+ const listener = {
1365
+ eventType,
1366
+ fn
1367
+ };
1368
+ this.subscribers.add(listener);
1369
+ return () => {
1370
+ this.subscribers.delete(listener);
1371
+ };
1372
+ };
1373
+ emit = routerEvent => {
1374
+ this.subscribers.forEach(listener => {
1375
+ if (listener.eventType === routerEvent.type) {
1376
+ listener.fn(routerEvent);
1377
+ }
1378
+ });
1379
+ };
1380
+ checkLatest = promise => {
1381
+ return this.latestLoadPromise !== promise ? this.latestLoadPromise : undefined;
1382
+ };
1383
+ parseLocation = previousLocation => {
1384
+ const parse = ({
1385
+ pathname,
1386
+ search,
1387
+ hash,
1388
+ state
1389
+ }) => {
1390
+ const parsedSearch = this.options.parseSearch(search);
1391
+ return {
1392
+ pathname: pathname,
1393
+ searchStr: search,
1394
+ search: replaceEqualDeep(previousLocation?.search, parsedSearch),
1395
+ hash: hash.split('#').reverse()[0] ?? '',
1396
+ href: `${pathname}${search}${hash}`,
1397
+ state: replaceEqualDeep(previousLocation?.state, state)
1398
+ };
1399
+ };
1400
+ const location = parse(this.history.location);
1401
+ let {
1402
+ __tempLocation,
1403
+ __tempKey
1404
+ } = location.state;
1405
+ if (__tempLocation && (!__tempKey || __tempKey === this.tempLocationKey)) {
1406
+ // Sync up the location keys
1407
+ const parsedTempLocation = parse(__tempLocation);
1408
+ parsedTempLocation.state.key = location.state.key;
1409
+ delete parsedTempLocation.state.__tempLocation;
1410
+ return {
1411
+ ...parsedTempLocation,
1412
+ maskedLocation: location
1413
+ };
1414
+ }
1415
+ return location;
1416
+ };
1417
+ resolvePathWithBase = (from, path) => {
1418
+ return resolvePath(this.basepath, from, cleanPath(path));
1419
+ };
1420
+ get looseRoutesById() {
1421
+ return this.routesById;
1422
+ }
1423
+ matchRoutes = (pathname, locationSearch, opts) => {
1424
+ let routeParams = {};
1425
+ let foundRoute = this.flatRoutes.find(route => {
1426
+ const matchedParams = matchPathname(this.basepath, trimPathRight(pathname), {
1427
+ to: route.fullPath,
1428
+ caseSensitive: route.options.caseSensitive ?? this.options.caseSensitive,
1429
+ fuzzy: false
2011
1430
  });
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;
1431
+ if (matchedParams) {
1432
+ routeParams = matchedParams;
1433
+ return true;
2022
1434
  }
1435
+ return false;
1436
+ });
1437
+ let routeCursor = foundRoute || this.routesById['__root__'];
1438
+ let matchedRoutes = [routeCursor];
1439
+ // let includingLayouts = true
1440
+ while (routeCursor?.parentRoute) {
1441
+ routeCursor = routeCursor.parentRoute;
1442
+ if (routeCursor) matchedRoutes.unshift(routeCursor);
1443
+ }
2023
1444
 
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
- }
1445
+ // Existing matches are matches that are already loaded along with
1446
+ // pending matches that are still loading
2068
1447
 
2069
- return !!foundRoutes.length;
1448
+ const parseErrors = matchedRoutes.map(route => {
1449
+ let parsedParamsError;
1450
+ if (route.options.parseParams) {
1451
+ try {
1452
+ const parsedParams = route.options.parseParams(routeParams);
1453
+ // Add the parsed params to the accumulated params bag
1454
+ Object.assign(routeParams, parsedParams);
1455
+ } catch (err) {
1456
+ parsedParamsError = new PathParamError(err.message, {
1457
+ cause: err
2070
1458
  });
2071
- return !!foundRoutes.length;
2072
- };
2073
-
2074
- findMatchInRoutes([], filteredRoutes);
2075
-
2076
- if (!foundRoutes.length) {
2077
- return;
1459
+ if (opts?.throwOnError) {
1460
+ throw parsedParamsError;
1461
+ }
1462
+ return parsedParamsError;
2078
1463
  }
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])
1464
+ }
1465
+ return;
1466
+ });
1467
+ const matches = [];
1468
+ matchedRoutes.forEach((route, index) => {
1469
+ // Take each matched route and resolve + validate its search params
1470
+ // This has to happen serially because each route's search params
1471
+ // can depend on the parent route's search params
1472
+ // It must also happen before we create the match so that we can
1473
+ // pass the search params to the route's potential key function
1474
+ // which is used to uniquely identify the route match in state
1475
+
1476
+ const parentMatch = matches[index - 1];
1477
+ const [preMatchSearch, searchError] = (() => {
1478
+ // Validate the search params and stabilize them
1479
+ const parentSearch = parentMatch?.search ?? locationSearch;
1480
+ try {
1481
+ const validator = typeof route.options.validateSearch === 'object' ? route.options.validateSearch.parse : route.options.validateSearch;
1482
+ let search = validator?.(parentSearch) ?? {};
1483
+ return [{
1484
+ ...parentSearch,
1485
+ ...search
1486
+ }, undefined];
1487
+ } catch (err) {
1488
+ const searchError = new SearchParamError(err.message, {
1489
+ cause: err
2090
1490
  });
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);
1491
+ if (opts?.throwOnError) {
1492
+ throw searchError;
1493
+ }
1494
+ return [parentSearch, searchError];
2097
1495
  }
1496
+ })();
1497
+ const interpolatedPath = interpolatePath(route.path, routeParams);
1498
+ const matchId = interpolatePath(route.id, routeParams, true) + (route.options.key?.({
1499
+ search: preMatchSearch,
1500
+ location: this.state.location
1501
+ }) ?? '');
1502
+
1503
+ // Waste not, want not. If we already have a match for this route,
1504
+ // reuse it. This is important for layout routes, which might stick
1505
+ // around between navigation actions that only change leaf routes.
1506
+ const existingMatch = getRouteMatch(this.state, matchId);
1507
+ const cause = this.state.matches.find(d => d.id === matchId) ? 'stay' : 'enter';
1508
+
1509
+ // Create a fresh route match
1510
+ const hasLoaders = !!(route.options.loader || componentTypes.some(d => route.options[d]?.preload));
1511
+ const match = existingMatch ? {
1512
+ ...existingMatch,
1513
+ cause
1514
+ } : {
1515
+ id: matchId,
1516
+ routeId: route.id,
1517
+ params: routeParams,
1518
+ pathname: joinPaths([this.basepath, interpolatedPath]),
1519
+ updatedAt: Date.now(),
1520
+ search: {},
1521
+ searchError: undefined,
1522
+ status: hasLoaders ? 'pending' : 'success',
1523
+ showPending: false,
1524
+ isFetching: false,
1525
+ invalid: false,
1526
+ error: undefined,
1527
+ paramsError: parseErrors[index],
1528
+ loadPromise: Promise.resolve(),
1529
+ context: undefined,
1530
+ abortController: new AbortController(),
1531
+ shouldReloadDeps: undefined,
1532
+ fetchedAt: 0,
1533
+ cause
2098
1534
  };
2099
1535
 
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
-
1536
+ // Regardless of whether we're reusing an existing match or creating
1537
+ // a new one, we need to update the match's search params
1538
+ match.search = replaceEqualDeep(match.search, preMatchSearch);
1539
+ // And also update the searchError if there is one
1540
+ match.searchError = searchError;
1541
+ matches.push(match);
1542
+ });
1543
+ return matches;
1544
+ };
1545
+ cancelMatch = id => {
1546
+ getRouteMatch(this.state, id)?.abortController?.abort();
1547
+ };
1548
+ cancelMatches = () => {
1549
+ this.state.matches.forEach(match => {
1550
+ this.cancelMatch(match.id);
1551
+ });
1552
+ };
1553
+ buildLocation = opts => {
1554
+ const build = (dest = {}, matches) => {
1555
+ const from = this.latestLocation;
1556
+ const fromPathname = dest.from ?? from.pathname;
1557
+ let pathname = this.resolvePathWithBase(fromPathname, `${dest.to ?? ''}`);
1558
+ const fromMatches = this.matchRoutes(fromPathname, from.search);
1559
+ const stayingMatches = matches?.filter(d => fromMatches?.find(e => e.routeId === d.routeId));
1560
+ const prevParams = {
1561
+ ...last(fromMatches)?.params
1562
+ };
1563
+ let nextParams = (dest.params ?? true) === true ? prevParams : functionalUpdate(dest.params, prevParams);
1564
+ if (nextParams) {
1565
+ matches?.map(d => this.looseRoutesById[d.routeId].options.stringifyParams).filter(Boolean).forEach(fn => {
1566
+ nextParams = {
1567
+ ...nextParams,
1568
+ ...fn(nextParams)
1569
+ };
2146
1570
  });
2147
-
2148
- if (res.ok) {
2149
- return res.json();
2150
- }
2151
-
2152
- throw new Error('Failed to fetch match data');
2153
1571
  }
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)) {
1572
+ pathname = interpolatePath(pathname, nextParams ?? {});
1573
+ const preSearchFilters = stayingMatches?.map(match => this.looseRoutesById[match.routeId].options.preSearchFilters ?? []).flat().filter(Boolean) ?? [];
1574
+ const postSearchFilters = stayingMatches?.map(match => this.looseRoutesById[match.routeId].options.postSearchFilters ?? []).flat().filter(Boolean) ?? [];
1575
+
1576
+ // Pre filters first
1577
+ const preFilteredSearch = preSearchFilters?.length ? preSearchFilters?.reduce((prev, next) => next(prev), from.search) : from.search;
1578
+
1579
+ // Then the link/navigate function
1580
+ const destSearch = dest.search === true ? preFilteredSearch // Preserve resolvedFrom true
1581
+ : dest.search ? functionalUpdate(dest.search, preFilteredSearch) ?? {} // Updater
1582
+ : preSearchFilters?.length ? preFilteredSearch // Preserve resolvedFrom filters
1583
+ : {};
1584
+
1585
+ // Then post filters
1586
+ const postFilteredSearch = postSearchFilters?.length ? postSearchFilters.reduce((prev, next) => next(prev), destSearch) : destSearch;
1587
+ const search = replaceEqualDeep(from.search, postFilteredSearch);
1588
+ const searchStr = this.options.stringifySearch(search);
1589
+ const hash = dest.hash === true ? from.hash : dest.hash ? functionalUpdate(dest.hash, from.hash) : from.hash;
1590
+ const hashStr = hash ? `#${hash}` : '';
1591
+ let nextState = dest.state === true ? from.state : dest.state ? functionalUpdate(dest.state, from.state) : from.state;
1592
+ nextState = replaceEqualDeep(from.state, nextState);
1593
+ return {
1594
+ pathname,
1595
+ search,
1596
+ searchStr,
1597
+ state: nextState,
1598
+ hash,
1599
+ href: this.history.createHref(`${pathname}${searchStr}${hashStr}`),
1600
+ unmaskOnReload: dest.unmaskOnReload
1601
+ };
1602
+ };
1603
+ const buildWithMatches = (dest = {}, maskedDest) => {
1604
+ let next = build(dest);
1605
+ let maskedNext = maskedDest ? build(maskedDest) : undefined;
1606
+ if (!maskedNext) {
1607
+ let params = {};
1608
+ let foundMask = this.options.routeMasks?.find(d => {
1609
+ const match = matchPathname(this.basepath, next.pathname, {
1610
+ to: d.from,
1611
+ caseSensitive: false,
1612
+ fuzzy: false
1613
+ });
1614
+ if (match) {
1615
+ params = match;
1616
+ return true;
1617
+ }
2187
1618
  return false;
1619
+ });
1620
+ if (foundMask) {
1621
+ foundMask = {
1622
+ ...foundMask,
1623
+ from: interpolatePath(foundMask.from, params)
1624
+ };
1625
+ maskedDest = foundMask;
1626
+ maskedNext = build(maskedDest);
2188
1627
  }
2189
-
2190
- return !!matchPathname(router.state.pending.location.pathname, _extends({}, opts, {
2191
- to: next.pathname
2192
- }));
2193
1628
  }
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
1629
+ const nextMatches = this.matchRoutes(next.pathname, next.search);
1630
+ const maskedMatches = maskedNext ? this.matchRoutes(maskedNext.pathname, maskedNext.search) : undefined;
1631
+ const maskedFinal = maskedNext ? build(maskedDest, maskedMatches) : undefined;
1632
+ const final = build(dest, nextMatches);
1633
+ if (maskedFinal) {
1634
+ final.maskedLocation = maskedFinal;
1635
+ }
1636
+ return final;
1637
+ };
1638
+ if (opts.mask) {
1639
+ return buildWithMatches(opts, {
1640
+ ...pick(opts, ['from']),
1641
+ ...opts.mask
2229
1642
  });
2230
- },
2231
- buildLink: _ref8 => {
2232
- var _preload, _ref9;
2233
-
1643
+ }
1644
+ return buildWithMatches(opts);
1645
+ };
1646
+ commitLocation = async ({
1647
+ startTransition,
1648
+ ...next
1649
+ }) => {
1650
+ if (this.navigateTimeout) clearTimeout(this.navigateTimeout);
1651
+ const isSameUrl = this.latestLocation.href === next.href;
1652
+
1653
+ // If the next urls are the same and we're not replacing,
1654
+ // do nothing
1655
+ if (!isSameUrl || !next.replace) {
2234
1656
  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
1657
+ maskedLocation,
1658
+ ...nextHistory
1659
+ } = next;
1660
+ if (maskedLocation) {
1661
+ nextHistory = {
1662
+ ...maskedLocation,
1663
+ state: {
1664
+ ...maskedLocation.state,
1665
+ __tempKey: undefined,
1666
+ __tempLocation: {
1667
+ ...nextHistory,
1668
+ search: nextHistory.searchStr,
1669
+ state: {
1670
+ ...nextHistory.state,
1671
+ __tempKey: undefined,
1672
+ __tempLocation: undefined,
1673
+ key: undefined
1674
+ }
1675
+ }
1676
+ }
2259
1677
  };
2260
- } catch (e) {}
2261
-
2262
- const nextOpts = {
2263
- from,
2264
- to,
2265
- search,
2266
- params,
2267
- hash,
2268
- replace
2269
- };
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
2304
- });
1678
+ if (nextHistory.unmaskOnReload ?? this.options.unmaskOnReload ?? false) {
1679
+ nextHistory.state.__tempKey = this.tempLocationKey;
2305
1680
  }
1681
+ }
1682
+ const apply = () => {
1683
+ this.history[next.replace ? 'replace' : 'push'](nextHistory.href, nextHistory.state);
2306
1684
  };
2307
-
2308
- const handleEnter = e => {
2309
- const target = e.target || {};
2310
-
2311
- if (preload) {
2312
- if (target.preloadTimeout) {
2313
- return;
1685
+ if (startTransition ?? true) {
1686
+ this.startReactTransition(apply);
1687
+ } else {
1688
+ apply();
1689
+ }
1690
+ }
1691
+ this.resetNextScroll = next.resetScroll ?? true;
1692
+ return this.latestLoadPromise;
1693
+ };
1694
+ buildAndCommitLocation = ({
1695
+ replace,
1696
+ resetScroll,
1697
+ startTransition,
1698
+ ...rest
1699
+ } = {}) => {
1700
+ const location = this.buildLocation(rest);
1701
+ return this.commitLocation({
1702
+ ...location,
1703
+ startTransition,
1704
+ replace,
1705
+ resetScroll
1706
+ });
1707
+ };
1708
+ navigate = ({
1709
+ from,
1710
+ to = '',
1711
+ ...rest
1712
+ }) => {
1713
+ // If this link simply reloads the current route,
1714
+ // make sure it has a new key so it will trigger a data refresh
1715
+
1716
+ // If this `to` is a valid external URL, return
1717
+ // null for LinkUtils
1718
+ const toString = String(to);
1719
+ const fromString = typeof from === 'undefined' ? from : String(from);
1720
+ let isExternal;
1721
+ try {
1722
+ new URL(`${toString}`);
1723
+ isExternal = true;
1724
+ } catch (e) {}
1725
+ invariant(!isExternal, 'Attempting to navigate to external url with this.navigate!');
1726
+ return this.buildAndCommitLocation({
1727
+ ...rest,
1728
+ from: fromString,
1729
+ to: toString
1730
+ });
1731
+ };
1732
+ loadMatches = async ({
1733
+ checkLatest,
1734
+ matches,
1735
+ preload,
1736
+ invalidate
1737
+ }) => {
1738
+ let latestPromise;
1739
+ let firstBadMatchIndex;
1740
+
1741
+ // Check each match middleware to see if the route can be accessed
1742
+ try {
1743
+ for (let [index, match] of matches.entries()) {
1744
+ const parentMatch = matches[index - 1];
1745
+ const route = this.looseRoutesById[match.routeId];
1746
+ const handleError = (err, code) => {
1747
+ err.routerCode = code;
1748
+ firstBadMatchIndex = firstBadMatchIndex ?? index;
1749
+ if (isRedirect(err)) {
1750
+ throw err;
2314
1751
  }
2315
-
2316
- target.preloadTimeout = setTimeout(() => {
2317
- target.preloadTimeout = null;
2318
- router.preloadRoute(nextOpts, {
2319
- maxAge: userPreloadMaxAge,
2320
- gcMaxAge: userPreloadGcMaxAge
2321
- });
2322
- }, preloadDelay);
2323
- }
2324
- };
2325
-
2326
- const handleLeave = e => {
2327
- const target = e.target || {};
2328
-
2329
- if (target.preloadTimeout) {
2330
- clearTimeout(target.preloadTimeout);
2331
- target.preloadTimeout = null;
2332
- }
2333
- };
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();
1752
+ try {
1753
+ route.options.onError?.(err);
1754
+ } catch (errorHandlerErr) {
1755
+ err = errorHandlerErr;
1756
+ if (isRedirect(errorHandlerErr)) {
1757
+ throw errorHandlerErr;
2382
1758
  }
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
- });
1759
+ }
1760
+ matches[index] = match = {
1761
+ ...match,
1762
+ error: err,
1763
+ status: 'error',
1764
+ updatedAt: Date.now()
1765
+ };
2388
1766
  };
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
1767
+ try {
1768
+ if (match.paramsError) {
1769
+ handleError(match.paramsError, 'PARSE_PARAMS');
1770
+ }
1771
+ if (match.searchError) {
1772
+ handleError(match.searchError, 'VALIDATE_SEARCH');
1773
+ }
1774
+ const parentContext = parentMatch?.context ?? this.options.context ?? {};
1775
+ const beforeLoadContext = (await route.options.beforeLoad?.({
1776
+ search: match.search,
1777
+ abortController: match.abortController,
1778
+ params: match.params,
1779
+ preload: !!preload,
1780
+ context: parentContext,
1781
+ location: this.state.location,
1782
+ // TOOD: just expose state and router, etc
1783
+ navigate: opts => this.navigate({
1784
+ ...opts,
1785
+ from: match.pathname
1786
+ }),
1787
+ buildLocation: this.buildLocation,
1788
+ cause: match.cause
1789
+ })) ?? {};
1790
+ const context = {
1791
+ ...parentContext,
1792
+ ...beforeLoadContext
1793
+ };
1794
+ matches[index] = match = {
1795
+ ...match,
1796
+ context: replaceEqualDeep(match.context, context)
1797
+ };
1798
+ } catch (err) {
1799
+ handleError(err, 'BEFORE_LOAD');
1800
+ break;
1801
+ }
1802
+ }
1803
+ } catch (err) {
1804
+ if (isRedirect(err)) {
1805
+ if (!preload) this.navigate(err);
1806
+ return matches;
1807
+ }
1808
+ throw err;
1809
+ }
1810
+ const validResolvedMatches = matches.slice(0, firstBadMatchIndex);
1811
+ const matchPromises = [];
1812
+ validResolvedMatches.forEach((match, index) => {
1813
+ matchPromises.push((async () => {
1814
+ const parentMatchPromise = matchPromises[index - 1];
1815
+ const route = this.looseRoutesById[match.routeId];
1816
+ const handleIfRedirect = err => {
1817
+ if (isRedirect(err)) {
1818
+ if (!preload) {
1819
+ this.navigate(err);
1820
+ }
1821
+ return true;
1822
+ }
1823
+ return false;
2405
1824
  };
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 = {};
1825
+ let loadPromise;
1826
+ matches[index] = match = {
1827
+ ...match,
1828
+ fetchedAt: Date.now(),
1829
+ invalid: false,
1830
+ showPending: false
1831
+ };
1832
+ const pendingMs = route.options.pendingMs ?? this.options.defaultPendingMs;
1833
+ let pendingPromise;
1834
+ if (!preload && pendingMs && (route.options.pendingComponent ?? this.options.defaultPendingComponent)) {
1835
+ pendingPromise = new Promise(r => setTimeout(r, pendingMs));
2416
1836
  }
1837
+ if (match.isFetching) {
1838
+ loadPromise = getRouteMatch(this.state, match.id)?.loadPromise;
1839
+ } else {
1840
+ const loaderContext = {
1841
+ params: match.params,
1842
+ search: match.search,
1843
+ preload: !!preload,
1844
+ parentMatchPromise,
1845
+ abortController: match.abortController,
1846
+ context: match.context,
1847
+ location: this.state.location,
1848
+ navigate: opts => this.navigate({
1849
+ ...opts,
1850
+ from: match.pathname
1851
+ }),
1852
+ cause: match.cause
1853
+ };
2417
1854
 
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);
1855
+ // Default to reloading the route all the time
1856
+ let shouldReload = true;
1857
+ let shouldReloadDeps = typeof route.options.shouldReload === 'function' ? route.options.shouldReload?.(loaderContext) : !!(route.options.shouldReload ?? true);
1858
+ if (match.cause === 'enter' || invalidate) {
1859
+ match.shouldReloadDeps = shouldReloadDeps;
1860
+ } else if (match.cause === 'stay') {
1861
+ if (typeof shouldReloadDeps === 'object') {
1862
+ // compare the deps to see if they've changed
1863
+ shouldReload = !deepEqual(shouldReloadDeps, match.shouldReloadDeps);
1864
+ match.shouldReloadDeps = shouldReloadDeps;
1865
+ } else {
1866
+ shouldReload = !!shouldReloadDeps;
1867
+ }
1868
+ }
2430
1869
 
2431
- let nextParams = ((_dest$params = dest.params) != null ? _dest$params : true) === true ? prevParams : functionalUpdate(dest.params, prevParams);
1870
+ // If the user doesn't want the route to reload, just
1871
+ // resolve with the existing loader data
2432
1872
 
2433
- if (nextParams) {
2434
- toMatches.map(d => d.options.stringifyParams).filter(Boolean).forEach(fn => {
2435
- Object.assign({}, nextParams, fn(nextParams));
2436
- });
1873
+ if (!shouldReload) {
1874
+ loadPromise = Promise.resolve(match.loaderData);
1875
+ } else {
1876
+ // Otherwise, load the route
1877
+ matches[index] = match = {
1878
+ ...match,
1879
+ isFetching: true
1880
+ };
1881
+ const componentsPromise = Promise.all(componentTypes.map(async type => {
1882
+ const component = route.options[type];
1883
+ if (component?.preload) {
1884
+ await component.preload();
1885
+ }
1886
+ }));
1887
+ const loaderPromise = route.options.loader?.(loaderContext);
1888
+ loadPromise = Promise.all([componentsPromise, loaderPromise]).then(d => d[1]);
1889
+ }
2437
1890
  }
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
1891
+ matches[index] = match = {
1892
+ ...match,
1893
+ loadPromise
2461
1894
  };
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';
2470
- }
2471
-
2472
- const isSameUrl = router.__.parseLocation(history.location).href === next.href;
2473
-
2474
- if (isSameUrl && !next.key) {
2475
- nextAction = 'replace';
2476
- }
2477
-
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
2493
- });
1895
+ if (!preload) {
1896
+ this.setState(s => ({
1897
+ ...s,
1898
+ matches: s.matches.map(d => d.id === match.id ? match : d)
1899
+ }));
2494
1900
  }
2495
-
2496
- router.navigationPromise = new Promise(resolve => {
2497
- const previousNavigationResolve = router.resolveNavigation;
2498
-
2499
- router.resolveNavigation = () => {
2500
- previousNavigationResolve();
2501
- resolve();
2502
- };
1901
+ let didShowPending = false;
1902
+ await new Promise(async resolve => {
1903
+ // If the route has a pending component and a pendingMs option,
1904
+ // forcefully show the pending component
1905
+ if (pendingPromise) {
1906
+ pendingPromise.then(() => {
1907
+ didShowPending = true;
1908
+ matches[index] = match = {
1909
+ ...match,
1910
+ showPending: true
1911
+ };
1912
+ this.setState(s => ({
1913
+ ...s,
1914
+ matches: s.matches.map(d => d.id === match.id ? match : d)
1915
+ }));
1916
+ resolve();
1917
+ });
1918
+ }
1919
+ try {
1920
+ const loaderData = await loadPromise;
1921
+ if (latestPromise = checkLatest()) return await latestPromise;
1922
+ const pendingMinMs = route.options.pendingMinMs ?? this.options.defaultPendingMinMs;
1923
+ if (didShowPending && pendingMinMs) {
1924
+ await new Promise(r => setTimeout(r, pendingMinMs));
1925
+ }
1926
+ matches[index] = match = {
1927
+ ...match,
1928
+ error: undefined,
1929
+ status: 'success',
1930
+ isFetching: false,
1931
+ updatedAt: Date.now(),
1932
+ loaderData,
1933
+ loadPromise: undefined
1934
+ };
1935
+ } catch (error) {
1936
+ if (latestPromise = checkLatest()) return await latestPromise;
1937
+ if (handleIfRedirect(error)) return;
1938
+ try {
1939
+ route.options.onError?.(error);
1940
+ } catch (onErrorError) {
1941
+ error = onErrorError;
1942
+ if (handleIfRedirect(onErrorError)) return;
1943
+ }
1944
+ matches[index] = match = {
1945
+ ...match,
1946
+ error,
1947
+ status: 'error',
1948
+ isFetching: false,
1949
+ updatedAt: Date.now()
1950
+ };
1951
+ }
1952
+ if (!preload) {
1953
+ this.setState(s => ({
1954
+ ...s,
1955
+ matches: s.matches.map(d => d.id === match.id ? match : d)
1956
+ }));
1957
+ }
1958
+ resolve();
2503
1959
  });
2504
- return router.navigationPromise;
2505
- }
2506
- }
1960
+ })());
1961
+ });
1962
+ await Promise.all(matchPromises);
1963
+ return matches;
2507
1964
  };
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));
2524
- }
2525
- });
2526
- }
2527
-
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));
1965
+ invalidate = () => this.load({
1966
+ invalidate: true
2540
1967
  });
2541
- const finalComp = forwardedComp;
2542
-
2543
- finalComp.preload = () => {
2544
- if (!promise) {
2545
- promise = importer().then(module => {
2546
- resolvedComp = module.default;
2547
- return resolvedComp;
1968
+ load = async opts => {
1969
+ const promise = new Promise(async (resolve, reject) => {
1970
+ const next = this.latestLocation;
1971
+ const prevLocation = this.state.resolvedLocation;
1972
+ const pathDidChange = prevLocation.href !== next.href;
1973
+ let latestPromise;
1974
+
1975
+ // Cancel any pending matches
1976
+ this.cancelMatches();
1977
+ this.emit({
1978
+ type: 'onBeforeLoad',
1979
+ fromLocation: prevLocation,
1980
+ toLocation: next,
1981
+ pathChanged: pathDidChange
2548
1982
  });
2549
- }
2550
-
2551
- return promise;
2552
- };
2553
-
2554
- return finalComp;
2555
- }
2556
- //
2557
- function Link(props) {
2558
- const router = useRouter();
2559
- return /*#__PURE__*/React.createElement(router.Link, props);
2560
- }
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);
2565
- }
2566
-
2567
- const useRouterSubscription = router => {
2568
- useSyncExternalStore(cb => router.subscribe(() => cb()), () => router.state, () => router.state);
2569
- };
2570
1983
 
2571
- function createReactRouter(opts) {
2572
- const makeRouteExt = (route, router) => {
2573
- return {
2574
- useRoute: function useRoute(subRouteId) {
2575
- if (subRouteId === void 0) {
2576
- subRouteId = '.';
1984
+ // Match the routes
1985
+ let matches = this.matchRoutes(next.pathname, next.search, {
1986
+ debug: true
1987
+ });
1988
+ this.pendingMatches = matches;
1989
+ const previousMatches = this.state.matches;
1990
+
1991
+ // Ingest the new matches
1992
+ this.setState(s => ({
1993
+ ...s,
1994
+ status: 'pending',
1995
+ location: next,
1996
+ matches
1997
+ }));
1998
+ try {
1999
+ try {
2000
+ // Load the matches
2001
+ await this.loadMatches({
2002
+ matches,
2003
+ checkLatest: () => this.checkLatest(promise),
2004
+ invalidate: opts?.invalidate
2005
+ });
2006
+ } catch (err) {
2007
+ // swallow this error, since we'll display the
2008
+ // errors on the route components
2577
2009
  }
2578
2010
 
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
- };
2011
+ // Only apply the latest transition
2012
+ if (latestPromise = this.checkLatest(promise)) {
2013
+ return latestPromise;
2615
2014
  }
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);
2015
+ const exitingMatchIds = previousMatches.filter(id => !this.pendingMatches.includes(id));
2016
+ const enteringMatchIds = this.pendingMatches.filter(id => !previousMatches.includes(id));
2017
+ const stayingMatchIds = previousMatches.filter(id => this.pendingMatches.includes(id))
2018
+
2019
+ // setState((s) => ({
2020
+ // ...s,
2021
+ // status: 'idle',
2022
+ // resolvedLocation: s.location,
2023
+ // }))
2024
+
2025
+ //
2026
+ ;
2027
+ [[exitingMatchIds, 'onLeave'], [enteringMatchIds, 'onEnter'], [stayingMatchIds, 'onTransition']].forEach(([matches, hook]) => {
2028
+ matches.forEach(match => {
2029
+ this.looseRoutesById[match.routeId].options[hook]?.(match);
2636
2030
  });
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
2031
  });
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
2668
- }));
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
2032
+ this.emit({
2033
+ type: 'onLoad',
2034
+ fromLocation: prevLocation,
2035
+ toLocation: next,
2036
+ pathChanged: pathDidChange
2680
2037
  });
2681
-
2682
- if (!params) {
2683
- return null;
2038
+ resolve();
2039
+ } catch (err) {
2040
+ // Only apply the latest transition
2041
+ if (latestPromise = this.checkLatest(promise)) {
2042
+ return latestPromise;
2684
2043
  }
2685
-
2686
- return typeof opts.children === 'function' ? opts.children(params) : opts.children;
2044
+ reject(err);
2687
2045
  }
2688
- };
2046
+ });
2047
+ this.latestLoadPromise = promise;
2048
+ return this.latestLoadPromise;
2689
2049
  };
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?");
2709
- }
2710
-
2711
- return match;
2712
- }
2050
+ preloadRoute = async (navigateOpts = this.state.location) => {
2051
+ let next = this.buildLocation(navigateOpts);
2052
+ let matches = this.matchRoutes(next.pathname, next.search, {
2053
+ throwOnError: true
2054
+ });
2055
+ await this.loadMatches({
2056
+ matches,
2057
+ preload: true,
2058
+ checkLatest: () => undefined
2059
+ });
2060
+ return [last(matches), matches];
2061
+ };
2062
+ buildLink = dest => {
2063
+ // If this link simply reloads the current route,
2064
+ // make sure it has a new key so it will trigger a data refresh
2065
+
2066
+ // If this `to` is a valid external URL, return
2067
+ // null for LinkUtils
2068
+
2069
+ const {
2070
+ to,
2071
+ preload: userPreload,
2072
+ preloadDelay: userPreloadDelay,
2073
+ activeOptions,
2074
+ disabled,
2075
+ target,
2076
+ replace,
2077
+ resetScroll,
2078
+ startTransition
2079
+ } = dest;
2080
+ try {
2081
+ new URL(`${to}`);
2082
+ return {
2083
+ type: 'external',
2084
+ href: to
2713
2085
  };
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()
2086
+ } catch (e) {}
2087
+ const nextOpts = dest;
2088
+ const next = this.buildLocation(nextOpts);
2089
+ const preload = userPreload ?? this.options.defaultPreload;
2090
+ const preloadDelay = userPreloadDelay ?? this.options.defaultPreloadDelay ?? 0;
2091
+
2092
+ // Compare path/hash for matches
2093
+ const currentPathSplit = this.latestLocation.pathname.split('/');
2094
+ const nextPathSplit = next.pathname.split('/');
2095
+ const pathIsFuzzyEqual = nextPathSplit.every((d, i) => d === currentPathSplit[i]);
2096
+ // Combine the matches based on user this.options
2097
+ const pathTest = activeOptions?.exact ? this.latestLocation.pathname === next.pathname : pathIsFuzzyEqual;
2098
+ const hashTest = activeOptions?.includeHash ? this.latestLocation.hash === next.hash : true;
2099
+ const searchTest = activeOptions?.includeSearch ?? true ? deepEqual(this.latestLocation.search, next.search, true) : true;
2100
+
2101
+ // The final "active" test
2102
+ const isActive = pathTest && hashTest && searchTest;
2103
+
2104
+ // The click handler
2105
+ const handleClick = e => {
2106
+ if (!disabled && !isCtrlEvent(e) && !e.defaultPrevented && (!target || target === '_self') && e.button === 0) {
2107
+ e.preventDefault();
2108
+
2109
+ // All is well? Navigate!
2110
+ this.commitLocation({
2111
+ ...next,
2112
+ replace,
2113
+ resetScroll,
2114
+ startTransition
2115
+ });
2728
2116
  }
2117
+ };
2729
2118
 
2730
- return component;
2119
+ // The click handler
2120
+ const handleFocus = e => {
2121
+ if (preload) {
2122
+ this.preloadRoute(nextOpts).catch(err => {
2123
+ console.warn(err);
2124
+ console.warn(preloadWarning);
2125
+ });
2126
+ }
2127
+ };
2128
+ const handleTouchStart = e => {
2129
+ this.preloadRoute(nextOpts).catch(err => {
2130
+ console.warn(err);
2131
+ console.warn(preloadWarning);
2132
+ });
2133
+ };
2134
+ const handleEnter = e => {
2135
+ const target = e.target || {};
2136
+ if (preload) {
2137
+ if (target.preloadTimeout) {
2138
+ return;
2139
+ }
2140
+ target.preloadTimeout = setTimeout(() => {
2141
+ target.preloadTimeout = null;
2142
+ this.preloadRoute(nextOpts).catch(err => {
2143
+ console.warn(err);
2144
+ console.warn(preloadWarning);
2145
+ });
2146
+ }, preloadDelay);
2147
+ }
2148
+ };
2149
+ const handleLeave = e => {
2150
+ const target = e.target || {};
2151
+ if (target.preloadTimeout) {
2152
+ clearTimeout(target.preloadTimeout);
2153
+ target.preloadTimeout = null;
2154
+ }
2155
+ };
2156
+ return {
2157
+ type: 'internal',
2158
+ next,
2159
+ handleFocus,
2160
+ handleClick,
2161
+ handleEnter,
2162
+ handleLeave,
2163
+ handleTouchStart,
2164
+ isActive,
2165
+ disabled
2166
+ };
2167
+ };
2168
+ matchRoute = (location, opts) => {
2169
+ location = {
2170
+ ...location,
2171
+ to: location.to ? this.resolvePathWithBase(location.from || '', location.to) : undefined
2172
+ };
2173
+ const next = this.buildLocation(location);
2174
+ if (opts?.pending && this.state.status !== 'pending') {
2175
+ return false;
2731
2176
  }
2732
- }));
2733
- return coreRouter;
2734
- }
2735
- function RouterProvider(_ref2) {
2736
- var _router$options$useCo;
2177
+ const baseLocation = opts?.pending ? this.latestLocation : this.state.resolvedLocation;
2737
2178
 
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
2179
+ // const baseLocation = state.resolvedLocation
2180
+
2181
+ if (!baseLocation) {
2182
+ return false;
2755
2183
  }
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;
2184
+ const match = matchPathname(this.basepath, baseLocation.pathname, {
2185
+ ...opts,
2186
+ to: next.pathname
2187
+ });
2188
+ if (!match) {
2189
+ return false;
2190
+ }
2191
+ if (match && (opts?.includeSearch ?? true)) {
2192
+ return deepEqual(baseLocation.search, next.search, true) ? match : false;
2193
+ }
2194
+ return match;
2195
+ };
2196
+ injectHtml = async html => {
2197
+ this.injectedHtml.push(html);
2198
+ };
2199
+ dehydrateData = (key, getData) => {
2200
+ if (typeof document === 'undefined') {
2201
+ const strKey = typeof key === 'string' ? key : JSON.stringify(key);
2202
+ this.injectHtml(async () => {
2203
+ const id = `__TSR_DEHYDRATED__${strKey}`;
2204
+ const data = typeof getData === 'function' ? await getData() : getData;
2205
+ return `<script id='${id}' suppressHydrationWarning>window["__TSR_DEHYDRATED__${escapeJSON(strKey)}"] = ${JSON.stringify(data)}
2206
+ ;(() => {
2207
+ var el = document.getElementById('${id}')
2208
+ el.parentElement.removeChild(el)
2209
+ })()
2210
+ </script>`;
2211
+ });
2212
+ return () => this.hydrateData(key);
2213
+ }
2214
+ return () => undefined;
2215
+ };
2216
+ hydrateData = key => {
2217
+ if (typeof document !== 'undefined') {
2218
+ const strKey = typeof key === 'string' ? key : JSON.stringify(key);
2219
+ return window[`__TSR_DEHYDRATED__${strKey}`];
2220
+ }
2221
+ return undefined;
2222
+ };
2775
2223
 
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);
2224
+ // dehydrate = (): DehydratedRouter => {
2225
+ // return {
2226
+ // state: {
2227
+ // dehydratedMatches: this.state.matches.map((d) =>
2228
+ // pick(d, ['fetchedAt', 'invalid', 'id', 'status', 'updatedAt']),
2229
+ // ),
2230
+ // },
2231
+ // }
2232
+ // }
2233
+
2234
+ // hydrate = async (__do_not_use_server_ctx?: HydrationCtx) => {
2235
+ // let _ctx = __do_not_use_server_ctx
2236
+ // // Client hydrates from window
2237
+ // if (typeof document !== 'undefined') {
2238
+ // _ctx = window.__TSR_DEHYDRATED__
2239
+ // }
2240
+
2241
+ // invariant(
2242
+ // _ctx,
2243
+ // 'Expected to find a __TSR_DEHYDRATED__ property on window... but we did not. Did you forget to render <DehydrateRouter /> in your app?',
2244
+ // )
2245
+
2246
+ // const ctx = _ctx
2247
+ // this.dehydratedData = ctx.payload as any
2248
+ // this.options.hydrate?.(ctx.payload as any)
2249
+ // const dehydratedState = ctx.router.state
2250
+
2251
+ // let matches = this.matchRoutes(
2252
+ // this.state.location.pathname,
2253
+ // this.state.location.search,
2254
+ // ).map((match) => {
2255
+ // const dehydratedMatch = dehydratedState.dehydratedMatches.find(
2256
+ // (d) => d.id === match.id,
2257
+ // )
2258
+
2259
+ // invariant(
2260
+ // dehydratedMatch,
2261
+ // `Could not find a client-side match for dehydrated match with id: ${match.id}!`,
2262
+ // )
2263
+
2264
+ // if (dehydratedMatch) {
2265
+ // return {
2266
+ // ...match,
2267
+ // ...dehydratedMatch,
2268
+ // }
2269
+ // }
2270
+ // return match
2271
+ // })
2272
+
2273
+ // this.setState((s) => {
2274
+ // return {
2275
+ // ...s,
2276
+ // matches: dehydratedState.dehydratedMatches as any,
2277
+ // }
2278
+ // })
2279
+ // }
2280
+
2281
+ // resolveMatchPromise = (matchId: string, key: string, value: any) => {
2282
+ // state.matches
2283
+ // .find((d) => d.id === matchId)
2284
+ // ?.__promisesByKey[key]?.resolve(value)
2285
+ // }
2783
2286
  }
2784
- function useSearch(_routeId) {
2785
- return useRouter().state.location.search;
2786
- }
2787
- function linkProps(props) {
2788
- const router = useRouter();
2789
- return router.linkProps(props);
2287
+
2288
+ // A function that takes an import() argument which is a function and returns a new function that will
2289
+ // proxy arguments from the caller to the imported function, retaining all type
2290
+ // information along the way
2291
+ function lazyFn(fn, key) {
2292
+ return async (...args) => {
2293
+ const imported = await fn();
2294
+ return imported[key || 'default'](...args);
2295
+ };
2790
2296
  }
2791
- function MatchRoute(props) {
2792
- const router = useRouter();
2793
- return /*#__PURE__*/React.createElement(router.MatchRoute, props);
2297
+ function isCtrlEvent(e) {
2298
+ return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
2794
2299
  }
2795
- function Outlet() {
2796
- var _ref3, _match$__$pendingComp, _match$__$errorCompon;
2797
2300
 
2301
+ const useLayoutEffect = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;
2302
+ const windowKey = 'window';
2303
+ const delimiter = '___';
2304
+ let weakScrolledElements = new WeakSet();
2305
+ let cache;
2306
+ const sessionsStorage = typeof window !== 'undefined' && window.sessionStorage;
2307
+ const defaultGetKey = location => location.state.key;
2308
+ function useScrollRestoration(options) {
2798
2309
  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;
2310
+ useLayoutEffect(() => {
2311
+ const getKey = options?.getKey || defaultGetKey;
2312
+ if (sessionsStorage) {
2313
+ if (!cache) {
2314
+ cache = (() => {
2315
+ const storageKey = 'tsr-scroll-restoration-v2';
2316
+ const state = JSON.parse(window.sessionStorage.getItem(storageKey) || 'null') || {
2317
+ cached: {},
2318
+ next: {}
2319
+ };
2320
+ return {
2321
+ state,
2322
+ set: updater => {
2323
+ cache.state = functionalUpdate(updater, cache.state);
2324
+ window.sessionStorage.setItem(storageKey, JSON.stringify(cache.state));
2325
+ }
2326
+ };
2327
+ })();
2328
+ }
2818
2329
  }
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);
2330
+ const {
2331
+ history
2332
+ } = window;
2333
+ if (history.scrollRestoration) {
2334
+ history.scrollRestoration = 'manual';
2824
2335
  }
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
2336
+ const onScroll = event => {
2337
+ if (weakScrolledElements.has(event.target)) return;
2338
+ weakScrolledElements.add(event.target);
2339
+ const elementSelector = event.target === document || event.target === window ? windowKey : getCssSelector(event.target);
2340
+ if (!cache.state.next[elementSelector]) {
2341
+ cache.set(c => ({
2342
+ ...c,
2343
+ next: {
2344
+ ...c.next,
2345
+ [elementSelector]: {
2346
+ scrollX: NaN,
2347
+ scrollY: NaN
2348
+ }
2349
+ }
2350
+ }));
2351
+ }
2835
2352
  };
2836
- }
2837
-
2838
- componentDidCatch(error, info) {
2839
- console.error(error);
2840
- this.setState({
2841
- error,
2842
- info
2843
- });
2844
- }
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);
2353
+ const getCssSelector = el => {
2354
+ let path = [],
2355
+ parent;
2356
+ while (parent = el.parentNode) {
2357
+ path.unshift(`${el.tagName}:nth-child(${[].indexOf.call(parent.children, el) + 1})`);
2358
+ el = parent;
2359
+ }
2360
+ return `${path.join(' > ')}`.toLowerCase();
2361
+ };
2362
+ if (typeof document !== 'undefined') {
2363
+ document.addEventListener('scroll', onScroll, true);
2853
2364
  }
2854
-
2855
- return this.props.children;
2856
- }
2857
-
2365
+ const unsubOnBeforeLoad = router.subscribe('onBeforeLoad', event => {
2366
+ if (event.pathChanged) {
2367
+ const restoreKey = getKey(event.fromLocation);
2368
+ for (const elementSelector in cache.state.next) {
2369
+ const entry = cache.state.next[elementSelector];
2370
+ if (elementSelector === windowKey) {
2371
+ entry.scrollX = window.scrollX || 0;
2372
+ entry.scrollY = window.scrollY || 0;
2373
+ } else if (elementSelector) {
2374
+ const element = document.querySelector(elementSelector);
2375
+ entry.scrollX = element?.scrollLeft || 0;
2376
+ entry.scrollY = element?.scrollTop || 0;
2377
+ }
2378
+ cache.set(c => {
2379
+ const next = {
2380
+ ...c.next
2381
+ };
2382
+ delete next[elementSelector];
2383
+ return {
2384
+ ...c,
2385
+ next,
2386
+ cached: {
2387
+ ...c.cached,
2388
+ [[restoreKey, elementSelector].join(delimiter)]: entry
2389
+ }
2390
+ };
2391
+ });
2392
+ }
2393
+ }
2394
+ });
2395
+ const unsubOnResolved = router.subscribe('onResolved', event => {
2396
+ if (event.pathChanged) {
2397
+ if (!router.resetNextScroll) {
2398
+ return;
2399
+ }
2400
+ router.resetNextScroll = true;
2401
+ const getKey = options?.getKey || defaultGetKey;
2402
+ const restoreKey = getKey(event.toLocation);
2403
+ let windowRestored = false;
2404
+ for (const cacheKey in cache.state.cached) {
2405
+ const entry = cache.state.cached[cacheKey];
2406
+ const [key, elementSelector] = cacheKey.split(delimiter);
2407
+ if (key === restoreKey) {
2408
+ if (elementSelector === windowKey) {
2409
+ windowRestored = true;
2410
+ window.scrollTo(entry.scrollX, entry.scrollY);
2411
+ } else if (elementSelector) {
2412
+ const element = document.querySelector(elementSelector);
2413
+ if (element) {
2414
+ element.scrollLeft = entry.scrollX;
2415
+ element.scrollTop = entry.scrollY;
2416
+ }
2417
+ }
2418
+ }
2419
+ }
2420
+ if (!windowRestored) {
2421
+ window.scrollTo(0, 0);
2422
+ }
2423
+ cache.set(c => ({
2424
+ ...c,
2425
+ next: {}
2426
+ }));
2427
+ weakScrolledElements = new WeakSet();
2428
+ }
2429
+ });
2430
+ return () => {
2431
+ document.removeEventListener('scroll', onScroll);
2432
+ unsubOnBeforeLoad();
2433
+ unsubOnResolved();
2434
+ };
2435
+ }, []);
2858
2436
  }
2859
-
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)));
2437
+ function ScrollRestoration(props) {
2438
+ useScrollRestoration(props);
2439
+ return null;
2886
2440
  }
2887
- function usePrompt(message, when) {
2888
- const router = useRouter();
2441
+
2442
+ function useBlocker(message, condition = true) {
2443
+ const {
2444
+ history
2445
+ } = useRouter();
2889
2446
  React.useEffect(() => {
2890
- if (!when) return;
2891
- let unblock = router.history.block(transition => {
2447
+ if (!condition) return;
2448
+ let unblock = history.block((retry, cancel) => {
2892
2449
  if (window.confirm(message)) {
2893
2450
  unblock();
2894
- transition.retry();
2895
- } else {
2896
- router.location.pathname = window.location.pathname;
2451
+ retry();
2897
2452
  }
2898
2453
  });
2899
2454
  return unblock;
2900
- }, [when, location, message]);
2455
+ });
2456
+ }
2457
+ function Block({
2458
+ message,
2459
+ condition,
2460
+ children
2461
+ }) {
2462
+ useBlocker(message, condition);
2463
+ return children ?? null;
2464
+ }
2465
+
2466
+ function useNavigate(defaultOpts) {
2467
+ const {
2468
+ navigate
2469
+ } = useRouter();
2470
+ const match = useMatch({
2471
+ strict: false
2472
+ });
2473
+ return React.useCallback(opts => {
2474
+ return navigate({
2475
+ from: opts?.to ? match.pathname : undefined,
2476
+ ...defaultOpts,
2477
+ ...opts
2478
+ });
2479
+ }, []);
2901
2480
  }
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;
2481
+ function typedNavigate(navigate) {
2482
+ return navigate;
2483
+ } //
2484
+
2485
+ function Navigate(props) {
2486
+ const {
2487
+ navigate
2488
+ } = useRouter();
2489
+ const match = useMatch({
2490
+ strict: false
2491
+ });
2492
+ useLayoutEffect$1(() => {
2493
+ navigate({
2494
+ from: props.to ? match.pathname : undefined,
2495
+ ...props
2496
+ });
2497
+ }, []);
2498
+ return null;
2910
2499
  }
2911
2500
 
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 };
2501
+ 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, useLayoutEffect$1 as useLayoutEffect, useLinkProps, useLoaderData, useMatch, useMatchRoute, useMatches, useNavigate, useParams, useRouteContext, useRouter, useRouterState, useScrollRestoration, useSearch, useStableCallback };
2913
2502
  //# sourceMappingURL=index.js.map