@tanstack/react-router 0.0.1-beta.83 → 1.0.0

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 (225) hide show
  1. package/LICENSE +1 -1
  2. package/build/cjs/CatchBoundary.js +128 -0
  3. package/build/cjs/CatchBoundary.js.map +1 -0
  4. package/build/cjs/Matches.js +233 -0
  5. package/build/cjs/Matches.js.map +1 -0
  6. package/build/cjs/RouterProvider.js +170 -0
  7. package/build/cjs/RouterProvider.js.map +1 -0
  8. package/build/cjs/_virtual/_rollupPluginBabelHelpers.js +2 -4
  9. package/build/cjs/_virtual/_rollupPluginBabelHelpers.js.map +1 -1
  10. package/build/cjs/_virtual/with-selector.development.js +16 -0
  11. package/build/cjs/_virtual/with-selector.development.js.map +1 -0
  12. package/build/cjs/_virtual/with-selector.js +16 -0
  13. package/build/cjs/_virtual/with-selector.js.map +1 -0
  14. package/build/cjs/_virtual/with-selector.production.min.js +16 -0
  15. package/build/cjs/_virtual/with-selector.production.min.js.map +1 -0
  16. package/build/cjs/awaited.js +43 -0
  17. package/build/cjs/awaited.js.map +1 -0
  18. package/build/cjs/build/esm/index.js +79 -0
  19. package/build/cjs/build/esm/index.js.map +1 -0
  20. package/build/cjs/defer.js +37 -0
  21. package/build/cjs/defer.js.map +1 -0
  22. package/build/cjs/fileRoute.js +27 -0
  23. package/build/cjs/fileRoute.js.map +1 -0
  24. package/build/cjs/index.js +113 -451
  25. package/build/cjs/index.js.map +1 -1
  26. package/build/cjs/lazyRouteComponent.js +54 -0
  27. package/build/cjs/lazyRouteComponent.js.map +1 -0
  28. package/build/cjs/link.js +223 -0
  29. package/build/cjs/link.js.map +1 -0
  30. package/build/cjs/node_modules/.pnpm/@tanstack_react-store@0.2.1_react-dom@18.2.0_react@18.2.0/node_modules/@tanstack/react-store/build/modern/index.js +47 -0
  31. package/build/cjs/node_modules/.pnpm/@tanstack_react-store@0.2.1_react-dom@18.2.0_react@18.2.0/node_modules/@tanstack/react-store/build/modern/index.js.map +1 -0
  32. package/build/cjs/node_modules/.pnpm/@tanstack_store@0.1.3/node_modules/@tanstack/store/build/modern/index.js +70 -0
  33. package/build/cjs/node_modules/.pnpm/@tanstack_store@0.1.3/node_modules/@tanstack/store/build/modern/index.js.map +1 -0
  34. package/build/cjs/node_modules/.pnpm/use-sync-external-store@1.2.0_react@18.2.0/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.development.js +188 -0
  35. package/build/cjs/node_modules/.pnpm/use-sync-external-store@1.2.0_react@18.2.0/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.development.js.map +1 -0
  36. package/build/cjs/node_modules/.pnpm/use-sync-external-store@1.2.0_react@18.2.0/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.production.min.js +39 -0
  37. package/build/cjs/node_modules/.pnpm/use-sync-external-store@1.2.0_react@18.2.0/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.production.min.js.map +1 -0
  38. package/build/cjs/node_modules/.pnpm/use-sync-external-store@1.2.0_react@18.2.0/node_modules/use-sync-external-store/shim/with-selector.js +26 -0
  39. package/build/cjs/node_modules/.pnpm/use-sync-external-store@1.2.0_react@18.2.0/node_modules/use-sync-external-store/shim/with-selector.js.map +1 -0
  40. package/build/cjs/packages/react-router/src/CatchBoundary.js +123 -0
  41. package/build/cjs/packages/react-router/src/CatchBoundary.js.map +1 -0
  42. package/build/cjs/packages/react-router/src/Matches.js +235 -0
  43. package/build/cjs/packages/react-router/src/Matches.js.map +1 -0
  44. package/build/cjs/packages/react-router/src/RouterProvider.js +144 -0
  45. package/build/cjs/packages/react-router/src/RouterProvider.js.map +1 -0
  46. package/build/cjs/packages/react-router/src/awaited.js +43 -0
  47. package/build/cjs/packages/react-router/src/awaited.js.map +1 -0
  48. package/build/cjs/packages/react-router/src/defer.js +37 -0
  49. package/build/cjs/packages/react-router/src/defer.js.map +1 -0
  50. package/build/cjs/packages/react-router/src/fileRoute.js +27 -0
  51. package/build/cjs/packages/react-router/src/fileRoute.js.map +1 -0
  52. package/build/cjs/packages/react-router/src/index.js +61 -0
  53. package/build/cjs/packages/react-router/src/index.js.map +1 -0
  54. package/build/cjs/packages/react-router/src/lazyRouteComponent.js +54 -0
  55. package/build/cjs/packages/react-router/src/lazyRouteComponent.js.map +1 -0
  56. package/build/cjs/packages/react-router/src/link.js +148 -0
  57. package/build/cjs/packages/react-router/src/link.js.map +1 -0
  58. package/build/cjs/packages/react-router/src/path.js +209 -0
  59. package/build/cjs/packages/react-router/src/path.js.map +1 -0
  60. package/build/cjs/packages/react-router/src/qss.js +63 -0
  61. package/build/cjs/packages/react-router/src/qss.js.map +1 -0
  62. package/build/cjs/packages/react-router/src/react.js +634 -0
  63. package/build/cjs/packages/react-router/src/react.js.map +1 -0
  64. package/build/cjs/packages/react-router/src/redirects.js +25 -0
  65. package/build/cjs/packages/react-router/src/redirects.js.map +1 -0
  66. package/build/cjs/packages/react-router/src/route.js +134 -0
  67. package/build/cjs/packages/react-router/src/route.js.map +1 -0
  68. package/build/cjs/packages/react-router/src/router.js +1111 -0
  69. package/build/cjs/packages/react-router/src/router.js.map +1 -0
  70. package/build/cjs/packages/react-router/src/scroll-restoration.js +53 -0
  71. package/build/cjs/packages/react-router/src/scroll-restoration.js.map +1 -0
  72. package/build/cjs/packages/react-router/src/searchParams.js +81 -0
  73. package/build/cjs/packages/react-router/src/searchParams.js.map +1 -0
  74. package/build/cjs/packages/react-router/src/useBlocker.js +61 -0
  75. package/build/cjs/packages/react-router/src/useBlocker.js.map +1 -0
  76. package/build/cjs/packages/react-router/src/useNavigate.js +75 -0
  77. package/build/cjs/packages/react-router/src/useNavigate.js.map +1 -0
  78. package/build/cjs/packages/react-router/src/useParams.js +26 -0
  79. package/build/cjs/packages/react-router/src/useParams.js.map +1 -0
  80. package/build/cjs/packages/react-router/src/useSearch.js +25 -0
  81. package/build/cjs/packages/react-router/src/useSearch.js.map +1 -0
  82. package/build/cjs/packages/react-router/src/utils.js +239 -0
  83. package/build/cjs/packages/react-router/src/utils.js.map +1 -0
  84. package/build/cjs/path.js +214 -0
  85. package/build/cjs/path.js.map +1 -0
  86. package/build/cjs/qss.js +63 -0
  87. package/build/cjs/qss.js.map +1 -0
  88. package/build/cjs/react/CatchBoundary.js +123 -0
  89. package/build/cjs/react/CatchBoundary.js.map +1 -0
  90. package/build/cjs/react/awaited.js +43 -0
  91. package/build/cjs/react/awaited.js.map +1 -0
  92. package/build/cjs/react/defer.js +37 -0
  93. package/build/cjs/react/defer.js.map +1 -0
  94. package/build/cjs/react.js +650 -0
  95. package/build/cjs/react.js.map +1 -0
  96. package/build/cjs/redirects.js +28 -0
  97. package/build/cjs/redirects.js.map +1 -0
  98. package/build/cjs/route.js +191 -0
  99. package/build/cjs/route.js.map +1 -0
  100. package/build/cjs/router.js +1085 -0
  101. package/build/cjs/router.js.map +1 -0
  102. package/build/cjs/routerConfig.js +209 -0
  103. package/build/cjs/routerConfig.js.map +1 -0
  104. package/build/cjs/scroll-restoration.js +202 -0
  105. package/build/cjs/scroll-restoration.js.map +1 -0
  106. package/build/cjs/searchParams.js +81 -0
  107. package/build/cjs/searchParams.js.map +1 -0
  108. package/build/cjs/src/CatchBoundary.js +126 -0
  109. package/build/cjs/src/CatchBoundary.js.map +1 -0
  110. package/build/cjs/src/Matches.js +235 -0
  111. package/build/cjs/src/Matches.js.map +1 -0
  112. package/build/cjs/src/RouterProvider.js +1051 -0
  113. package/build/cjs/src/RouterProvider.js.map +1 -0
  114. package/build/cjs/src/awaited.js +45 -0
  115. package/build/cjs/src/awaited.js.map +1 -0
  116. package/build/cjs/src/defer.js +39 -0
  117. package/build/cjs/src/defer.js.map +1 -0
  118. package/build/cjs/src/fileRoute.js +29 -0
  119. package/build/cjs/src/fileRoute.js.map +1 -0
  120. package/build/cjs/src/index.js +134 -0
  121. package/build/cjs/src/index.js.map +1 -0
  122. package/build/cjs/src/lazyRouteComponent.js +57 -0
  123. package/build/cjs/src/lazyRouteComponent.js.map +1 -0
  124. package/build/cjs/src/link.js +151 -0
  125. package/build/cjs/src/link.js.map +1 -0
  126. package/build/cjs/src/path.js +211 -0
  127. package/build/cjs/src/path.js.map +1 -0
  128. package/build/cjs/src/qss.js +65 -0
  129. package/build/cjs/src/qss.js.map +1 -0
  130. package/build/cjs/src/redirects.js +27 -0
  131. package/build/cjs/src/redirects.js.map +1 -0
  132. package/build/cjs/src/route.js +139 -0
  133. package/build/cjs/src/route.js.map +1 -0
  134. package/build/cjs/src/router.js +203 -0
  135. package/build/cjs/src/router.js.map +1 -0
  136. package/build/cjs/src/scroll-restoration.js +186 -0
  137. package/build/cjs/src/scroll-restoration.js.map +1 -0
  138. package/build/cjs/src/searchParams.js +83 -0
  139. package/build/cjs/src/searchParams.js.map +1 -0
  140. package/build/cjs/src/useBlocker.js +64 -0
  141. package/build/cjs/src/useBlocker.js.map +1 -0
  142. package/build/cjs/src/useNavigate.js +78 -0
  143. package/build/cjs/src/useNavigate.js.map +1 -0
  144. package/build/cjs/src/useParams.js +28 -0
  145. package/build/cjs/src/useParams.js.map +1 -0
  146. package/build/cjs/src/useSearch.js +27 -0
  147. package/build/cjs/src/useSearch.js.map +1 -0
  148. package/build/cjs/src/utils.js +230 -0
  149. package/build/cjs/src/utils.js.map +1 -0
  150. package/build/cjs/useBlocker.js +55 -0
  151. package/build/cjs/useBlocker.js.map +1 -0
  152. package/build/cjs/useNavigate.js +86 -0
  153. package/build/cjs/useNavigate.js.map +1 -0
  154. package/build/cjs/useParams.js +26 -0
  155. package/build/cjs/useParams.js.map +1 -0
  156. package/build/cjs/useSearch.js +25 -0
  157. package/build/cjs/useSearch.js.map +1 -0
  158. package/build/cjs/useStore.js +99 -0
  159. package/build/cjs/useStore.js.map +1 -0
  160. package/build/cjs/utils.js +241 -0
  161. package/build/cjs/utils.js.map +1 -0
  162. package/build/esm/index.js +2581 -337
  163. package/build/esm/index.js.map +1 -1
  164. package/build/stats-html.html +3494 -2700
  165. package/build/stats-react.json +1134 -87
  166. package/build/types/CatchBoundary.d.ts +36 -0
  167. package/build/types/Matches.d.ts +64 -0
  168. package/build/types/RouteMatch.d.ts +23 -0
  169. package/build/types/RouterProvider.d.ts +35 -0
  170. package/build/types/awaited.d.ts +9 -0
  171. package/build/types/defer.d.ts +19 -0
  172. package/build/types/fileRoute.d.ts +38 -0
  173. package/build/types/history.d.ts +7 -0
  174. package/build/types/index.d.ts +911 -88
  175. package/build/types/injectHtml.d.ts +0 -0
  176. package/build/types/lazyRouteComponent.d.ts +2 -0
  177. package/build/types/link.d.ts +93 -0
  178. package/build/types/location.d.ts +12 -0
  179. package/build/types/path.d.ts +17 -0
  180. package/build/types/qss.d.ts +2 -0
  181. package/build/types/react/CatchBoundary.d.ts +33 -0
  182. package/build/types/react/awaited.d.ts +9 -0
  183. package/build/types/react/defer.d.ts +19 -0
  184. package/build/types/react.d.ts +141 -0
  185. package/build/types/redirects.d.ts +11 -0
  186. package/build/types/route.d.ts +283 -0
  187. package/build/types/routeInfo.d.ts +31 -0
  188. package/build/types/router.d.ts +186 -0
  189. package/build/types/scroll-restoration.d.ts +18 -0
  190. package/build/types/searchParams.d.ts +7 -0
  191. package/build/types/useBlocker.d.ts +9 -0
  192. package/build/types/useNavigate.d.ts +19 -0
  193. package/build/types/useParams.d.ts +7 -0
  194. package/build/types/useSearch.d.ts +7 -0
  195. package/build/types/useStore.d.ts +12 -0
  196. package/build/types/utils.d.ts +69 -0
  197. package/build/umd/index.development.js +2829 -1796
  198. package/build/umd/index.development.js.map +1 -1
  199. package/build/umd/index.production.js +4 -24
  200. package/build/umd/index.production.js.map +1 -1
  201. package/package.json +9 -7
  202. package/src/CatchBoundary.tsx +101 -0
  203. package/src/Matches.tsx +423 -0
  204. package/src/RouterProvider.tsx +252 -0
  205. package/src/awaited.tsx +40 -0
  206. package/src/defer.ts +55 -0
  207. package/src/fileRoute.ts +152 -0
  208. package/src/history.ts +8 -0
  209. package/src/index.tsx +28 -747
  210. package/src/lazyRouteComponent.tsx +33 -0
  211. package/src/link.tsx +603 -0
  212. package/src/location.ts +13 -0
  213. package/src/path.ts +261 -0
  214. package/src/qss.ts +53 -0
  215. package/src/redirects.ts +39 -0
  216. package/src/route.ts +882 -0
  217. package/src/routeInfo.ts +84 -0
  218. package/src/router.ts +1671 -0
  219. package/src/scroll-restoration.tsx +230 -0
  220. package/src/searchParams.ts +79 -0
  221. package/src/useBlocker.tsx +27 -0
  222. package/src/useNavigate.tsx +111 -0
  223. package/src/useParams.tsx +25 -0
  224. package/src/useSearch.tsx +25 -0
  225. package/src/utils.ts +360 -0
@@ -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
  *
@@ -9,13 +9,12 @@
9
9
  * @license MIT
10
10
  */
11
11
  (function (global, factory) {
12
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react'), require('use-sync-external-store/shim/with-selector')) :
13
- typeof define === 'function' && define.amd ? define(['exports', 'react', 'use-sync-external-store/shim/with-selector'], factory) :
14
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ReactRouter = {}, global.React, global.withSelector));
15
- })(this, (function (exports, React, withSelector) { 'use strict';
12
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react'), require('use-sync-external-store/shim')) :
13
+ typeof define === 'function' && define.amd ? define(['exports', 'react', 'use-sync-external-store/shim'], factory) :
14
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ReactRouter = {}, global.React, global.require$$1));
15
+ })(this, (function (exports, React, require$$1) { 'use strict';
16
16
 
17
- function _interopNamespace(e) {
18
- if (e && e.__esModule) return e;
17
+ function _interopNamespaceDefault(e) {
19
18
  var n = Object.create(null);
20
19
  if (e) {
21
20
  Object.keys(e).forEach(function (k) {
@@ -28,147 +27,14 @@
28
27
  }
29
28
  });
30
29
  }
31
- n["default"] = e;
30
+ n.default = e;
32
31
  return Object.freeze(n);
33
32
  }
34
33
 
35
- var React__namespace = /*#__PURE__*/_interopNamespace(React);
36
-
37
- function _extends() {
38
- _extends = Object.assign ? Object.assign.bind() : function (target) {
39
- for (var i = 1; i < arguments.length; i++) {
40
- var source = arguments[i];
41
- for (var key in source) {
42
- if (Object.prototype.hasOwnProperty.call(source, key)) {
43
- target[key] = source[key];
44
- }
45
- }
46
- }
47
- return target;
48
- };
49
- return _extends.apply(this, arguments);
50
- }
51
-
52
- var prefix = 'Invariant failed';
53
- function invariant(condition, message) {
54
- if (condition) {
55
- return;
56
- }
57
- var provided = typeof message === 'function' ? message() : message;
58
- var value = provided ? "".concat(prefix, ": ").concat(provided) : prefix;
59
- throw new Error(value);
60
- }
61
-
62
- function warning(condition, message) {
63
- {
64
- if (condition) {
65
- return;
66
- }
67
-
68
- var text = "Warning: " + message;
69
-
70
- if (typeof console !== 'undefined') {
71
- console.warn(text);
72
- }
73
-
74
- try {
75
- throw Error(text);
76
- } catch (x) {}
77
- }
78
- }
79
-
80
- /**
81
- * store
82
- *
83
- * Copyright (c) TanStack
84
- *
85
- * This source code is licensed under the MIT license found in the
86
- * LICENSE.md file in the root directory of this source tree.
87
- *
88
- * @license MIT
89
- */
90
- class Store {
91
- listeners = new Set();
92
- batching = false;
93
- queue = [];
94
- constructor(initialState, options) {
95
- this.state = initialState;
96
- this.options = options;
97
- }
98
- subscribe = listener => {
99
- this.listeners.add(listener);
100
- const unsub = this.options?.onSubscribe?.(listener, this);
101
- return () => {
102
- this.listeners.delete(listener);
103
- unsub?.();
104
- };
105
- };
106
- setState = updater => {
107
- const previous = this.state;
108
- this.state = this.options?.updateFn ? this.options.updateFn(previous)(updater) : updater(previous);
109
- if (this.state === previous) return;
110
- this.options?.onUpdate?.(this.state, previous);
111
- this.queue.push(() => {
112
- this.listeners.forEach(listener => listener(this.state, previous));
113
- });
114
- this.#flush();
115
- };
116
- #flush = () => {
117
- if (this.batching) return;
118
- this.queue.forEach(cb => cb());
119
- this.queue = [];
120
- };
121
- batch = cb => {
122
- this.batching = true;
123
- cb();
124
- this.batching = false;
125
- this.#flush();
126
- };
127
- }
128
- function shallow(objA, objB) {
129
- if (Object.is(objA, objB)) {
130
- return true;
131
- }
132
- if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
133
- return false;
134
- }
135
-
136
- // if (objA instanceof Map && objB instanceof Map) {
137
- // if (objA.size !== objB.size) return false
138
-
139
- // for (const [key, value] of objA) {
140
- // if (!Object.is(value, objB.get(key))) {
141
- // return false
142
- // }
143
- // }
144
- // return true
145
- // }
146
-
147
- // if (objA instanceof Set && objB instanceof Set) {
148
- // if (objA.size !== objB.size) return false
149
-
150
- // for (const value of objA) {
151
- // if (!objB.has(value)) {
152
- // return false
153
- // }
154
- // }
155
- // return true
156
- // }
157
-
158
- const keysA = Object.keys(objA);
159
- if (keysA.length !== Object.keys(objB).length) {
160
- return false;
161
- }
162
- for (let i = 0; i < keysA.length; i++) {
163
- if (!Object.prototype.hasOwnProperty.call(objB, keysA[i]) || !Object.is(objA[keysA[i]], objB[keysA[i]])) {
164
- return false;
165
- }
166
- }
167
- return true;
168
- }
34
+ var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
169
35
 
170
36
  /**
171
- * router
37
+ * @tanstack/history/src/index.ts
172
38
  *
173
39
  * Copyright (c) TanStack
174
40
  *
@@ -177,11 +43,11 @@
177
43
  *
178
44
  * @license MIT
179
45
  */
180
-
181
46
  // While the public API was clearly inspired by the "history" npm package,
182
47
  // This implementation attempts to be more lightweight by
183
48
  // making assumptions about the way TanStack Router works
184
49
 
50
+ const pushStateEvent = 'pushstate';
185
51
  const popStateEvent = 'popstate';
186
52
  const beforeUnloadEvent = 'beforeunload';
187
53
  const beforeUnloadListener = event => {
@@ -195,124 +61,226 @@
195
61
  });
196
62
  };
197
63
  function createHistory(opts) {
198
- let currentLocation = opts.getLocation();
199
- let unsub = () => {};
200
- let listeners = new Set();
64
+ let location = opts.getLocation();
65
+ let subscribers = new Set();
201
66
  let blockers = [];
202
- let queue = [];
203
- const tryFlush = () => {
204
- if (blockers.length) {
205
- blockers[0]?.(tryFlush, () => {
206
- blockers = [];
207
- stopBlocking();
208
- });
209
- return;
210
- }
211
- while (queue.length) {
212
- queue.shift()?.();
213
- }
214
- onUpdate();
215
- };
216
- const queueTask = task => {
217
- queue.push(task);
218
- tryFlush();
219
- };
220
67
  const onUpdate = () => {
221
- currentLocation = opts.getLocation();
222
- listeners.forEach(listener => listener());
68
+ location = opts.getLocation();
69
+ subscribers.forEach(subscriber => subscriber());
70
+ };
71
+ const tryNavigation = async task => {
72
+ if (typeof document !== 'undefined' && blockers.length) {
73
+ for (let blocker of blockers) {
74
+ const allowed = await blocker();
75
+ if (!allowed) {
76
+ opts.onBlocked?.(onUpdate);
77
+ return;
78
+ }
79
+ }
80
+ }
81
+ task();
223
82
  };
224
83
  return {
225
84
  get location() {
226
- return currentLocation;
85
+ return location;
227
86
  },
228
- listen: cb => {
229
- if (listeners.size === 0) {
230
- unsub = opts.listener(onUpdate);
231
- }
232
- listeners.add(cb);
87
+ subscribe: cb => {
88
+ subscribers.add(cb);
233
89
  return () => {
234
- listeners.delete(cb);
235
- if (listeners.size === 0) {
236
- unsub();
237
- }
90
+ subscribers.delete(cb);
238
91
  };
239
92
  },
240
93
  push: (path, state) => {
241
- queueTask(() => {
242
- opts.pushState(path, state);
94
+ state = assignKey(state);
95
+ tryNavigation(() => {
96
+ opts.pushState(path, state, onUpdate);
243
97
  });
244
98
  },
245
99
  replace: (path, state) => {
246
- queueTask(() => {
247
- opts.replaceState(path, state);
100
+ state = assignKey(state);
101
+ tryNavigation(() => {
102
+ opts.replaceState(path, state, onUpdate);
248
103
  });
249
104
  },
250
105
  go: index => {
251
- queueTask(() => {
106
+ tryNavigation(() => {
252
107
  opts.go(index);
253
108
  });
254
109
  },
255
110
  back: () => {
256
- queueTask(() => {
111
+ tryNavigation(() => {
257
112
  opts.back();
258
113
  });
259
114
  },
260
115
  forward: () => {
261
- queueTask(() => {
116
+ tryNavigation(() => {
262
117
  opts.forward();
263
118
  });
264
119
  },
265
120
  createHref: str => opts.createHref(str),
266
- block: cb => {
267
- blockers.push(cb);
121
+ block: blocker => {
122
+ blockers.push(blocker);
268
123
  if (blockers.length === 1) {
269
124
  addEventListener(beforeUnloadEvent, beforeUnloadListener, {
270
125
  capture: true
271
126
  });
272
127
  }
273
128
  return () => {
274
- blockers = blockers.filter(b => b !== cb);
129
+ blockers = blockers.filter(b => b !== blocker);
275
130
  if (!blockers.length) {
276
131
  stopBlocking();
277
132
  }
278
133
  };
279
- }
134
+ },
135
+ flush: () => opts.flush?.(),
136
+ destroy: () => opts.destroy?.(),
137
+ notify: onUpdate
138
+ };
139
+ }
140
+ function assignKey(state) {
141
+ if (!state) {
142
+ state = {};
143
+ }
144
+ return {
145
+ ...state,
146
+ key: createRandomKey()
280
147
  };
281
148
  }
149
+
150
+ /**
151
+ * Creates a history object that can be used to interact with the browser's
152
+ * navigation. This is a lightweight API wrapping the browser's native methods.
153
+ * It is designed to work with TanStack Router, but could be used as a standalone API as well.
154
+ * IMPORTANT: This API implements history throttling via a microtask to prevent
155
+ * excessive calls to the history API. In some browsers, calling history.pushState or
156
+ * history.replaceState in quick succession can cause the browser to ignore subsequent
157
+ * calls. This API smooths out those differences and ensures that your application
158
+ * state will *eventually* match the browser state. In most cases, this is not a problem,
159
+ * but if you need to ensure that the browser state is up to date, you can use the
160
+ * `history.flush` method to immediately flush all pending state changes to the browser URL.
161
+ * @param opts
162
+ * @param opts.getHref A function that returns the current href (path + search + hash)
163
+ * @param opts.createHref A function that takes a path and returns a href (path + search + hash)
164
+ * @returns A history instance
165
+ */
282
166
  function createBrowserHistory(opts) {
283
- const getHref = opts?.getHref ?? (() => `${window.location.pathname}${window.location.hash}${window.location.search}`);
167
+ const win = opts?.window ?? (typeof document !== 'undefined' ? window : undefined);
284
168
  const createHref = opts?.createHref ?? (path => path);
285
- const getLocation = () => parseLocation(getHref(), history.state);
286
- return createHistory({
169
+ const parseLocation = opts?.parseLocation ?? (() => parseHref(`${win.location.pathname}${win.location.search}${win.location.hash}`, win.history.state));
170
+ let currentLocation = parseLocation();
171
+ let rollbackLocation;
172
+ const getLocation = () => currentLocation;
173
+ let next;
174
+
175
+ // Because we are proactively updating the location
176
+ // in memory before actually updating the browser history,
177
+ // we need to track when we are doing this so we don't
178
+ // notify subscribers twice on the last update.
179
+ let tracking = true;
180
+
181
+ // We need to track the current scheduled update to prevent
182
+ // multiple updates from being scheduled at the same time.
183
+ let scheduled;
184
+
185
+ // This function is a wrapper to prevent any of the callback's
186
+ // side effects from causing a subscriber notification
187
+ const untrack = fn => {
188
+ tracking = false;
189
+ fn();
190
+ tracking = true;
191
+ };
192
+
193
+ // This function flushes the next update to the browser history
194
+ const flush = () => {
195
+ // Do not notify subscribers about this push/replace call
196
+ untrack(() => {
197
+ if (!next) return;
198
+ win.history[next.isPush ? 'pushState' : 'replaceState'](next.state, '', next.href);
199
+ // Reset the nextIsPush flag and clear the scheduled update
200
+ next = undefined;
201
+ scheduled = undefined;
202
+ rollbackLocation = undefined;
203
+ });
204
+ };
205
+
206
+ // This function queues up a call to update the browser history
207
+ const queueHistoryAction = (type, destHref, state, onUpdate) => {
208
+ const href = createHref(destHref);
209
+ if (!scheduled) {
210
+ rollbackLocation = currentLocation;
211
+ }
212
+
213
+ // Update the location in memory
214
+ currentLocation = parseHref(destHref, state);
215
+
216
+ // Keep track of the next location we need to flush to the URL
217
+ next = {
218
+ href,
219
+ state,
220
+ isPush: next?.isPush || type === 'push'
221
+ };
222
+
223
+ // Notify subscribers
224
+ onUpdate();
225
+ if (!scheduled) {
226
+ // Schedule an update to the browser history
227
+ scheduled = Promise.resolve().then(() => flush());
228
+ }
229
+ };
230
+ const onPushPop = () => {
231
+ currentLocation = parseLocation();
232
+ history.notify();
233
+ };
234
+ var originalPushState = win.history.pushState;
235
+ var originalReplaceState = win.history.replaceState;
236
+ const history = createHistory({
287
237
  getLocation,
288
- listener: onUpdate => {
289
- window.addEventListener(popStateEvent, onUpdate);
290
- return () => {
291
- window.removeEventListener(popStateEvent, onUpdate);
292
- };
293
- },
294
- pushState: (path, state) => {
295
- window.history.pushState({
296
- ...state,
297
- key: createRandomKey()
298
- }, '', createHref(path));
299
- },
300
- replaceState: (path, state) => {
301
- window.history.replaceState({
302
- ...state,
303
- key: createRandomKey()
304
- }, '', createHref(path));
238
+ pushState: (href, state, onUpdate) => queueHistoryAction('push', href, state, onUpdate),
239
+ replaceState: (href, state, onUpdate) => queueHistoryAction('replace', href, state, onUpdate),
240
+ back: () => win.history.back(),
241
+ forward: () => win.history.forward(),
242
+ go: n => win.history.go(n),
243
+ createHref: href => createHref(href),
244
+ flush,
245
+ destroy: () => {
246
+ win.history.pushState = originalPushState;
247
+ win.history.replaceState = originalReplaceState;
248
+ win.removeEventListener(pushStateEvent, onPushPop);
249
+ win.removeEventListener(popStateEvent, onPushPop);
305
250
  },
306
- back: () => window.history.back(),
307
- forward: () => window.history.forward(),
308
- go: n => window.history.go(n),
309
- createHref: path => createHref(path)
251
+ onBlocked: onUpdate => {
252
+ // If a navigation is blocked, we need to rollback the location
253
+ // that we optimistically updated in memory.
254
+ if (rollbackLocation && currentLocation !== rollbackLocation) {
255
+ currentLocation = rollbackLocation;
256
+ // Notify subscribers
257
+ onUpdate();
258
+ }
259
+ }
310
260
  });
261
+ win.addEventListener(pushStateEvent, onPushPop);
262
+ win.addEventListener(popStateEvent, onPushPop);
263
+ win.history.pushState = function () {
264
+ let res = originalPushState.apply(win.history, arguments);
265
+ if (tracking) history.notify();
266
+ return res;
267
+ };
268
+ win.history.replaceState = function () {
269
+ let res = originalReplaceState.apply(win.history, arguments);
270
+ if (tracking) history.notify();
271
+ return res;
272
+ };
273
+ return history;
311
274
  }
312
- function createHashHistory() {
275
+ function createHashHistory(opts) {
276
+ const win = opts?.window ?? (typeof document !== 'undefined' ? window : undefined);
313
277
  return createBrowserHistory({
314
- getHref: () => window.location.hash.substring(1),
315
- createHref: path => `#${path}`
278
+ window: win,
279
+ parseLocation: () => {
280
+ const hashHref = win.location.hash.split('#').slice(1).join('#') ?? '/';
281
+ return parseHref(hashHref, win.history.state);
282
+ },
283
+ createHref: href => `${win.location.pathname}${win.location.search}#${href}`
316
284
  });
317
285
  }
318
286
  function createMemoryHistory(opts = {
@@ -320,26 +288,19 @@
320
288
  }) {
321
289
  const entries = opts.initialEntries;
322
290
  let index = opts.initialIndex ?? entries.length - 1;
323
- let currentState = {};
324
- const getLocation = () => parseLocation(entries[index], currentState);
291
+ let currentState = {
292
+ key: createRandomKey()
293
+ };
294
+ const getLocation = () => parseHref(entries[index], currentState);
325
295
  return createHistory({
326
296
  getLocation,
327
- listener: () => {
328
- return () => {};
329
- },
330
297
  pushState: (path, state) => {
331
- currentState = {
332
- ...state,
333
- key: createRandomKey()
334
- };
298
+ currentState = state;
335
299
  entries.push(path);
336
300
  index++;
337
301
  },
338
302
  replaceState: (path, state) => {
339
- currentState = {
340
- ...state,
341
- key: createRandomKey()
342
- };
303
+ currentState = state;
343
304
  entries[index] = path;
344
305
  },
345
306
  back: () => {
@@ -348,19 +309,21 @@
348
309
  forward: () => {
349
310
  index = Math.min(index + 1, entries.length - 1);
350
311
  },
351
- go: n => window.history.go(n),
312
+ go: n => {
313
+ index = Math.min(Math.max(index + n, 0), entries.length - 1);
314
+ },
352
315
  createHref: path => path
353
316
  });
354
317
  }
355
- function parseLocation(href, state) {
318
+ function parseHref(href, state) {
356
319
  let hashIndex = href.indexOf('#');
357
320
  let searchIndex = href.indexOf('?');
358
321
  return {
359
322
  href,
360
323
  pathname: href.substring(0, hashIndex > 0 ? searchIndex > 0 ? Math.min(hashIndex, searchIndex) : hashIndex : searchIndex > 0 ? searchIndex : href.length),
361
- hash: hashIndex > -1 ? href.substring(hashIndex, searchIndex) : '',
362
- search: searchIndex > -1 ? href.substring(searchIndex) : '',
363
- state
324
+ hash: hashIndex > -1 ? href.substring(hashIndex) : '',
325
+ search: searchIndex > -1 ? href.slice(searchIndex, hashIndex === -1 ? undefined : hashIndex) : '',
326
+ state: state || {}
364
327
  };
365
328
  }
366
329
 
@@ -369,98 +332,941 @@
369
332
  return (Math.random() + 1).toString(36).substring(7);
370
333
  }
371
334
 
372
- function last(arr) {
373
- return arr[arr.length - 1];
374
- }
375
- function isFunction(d) {
376
- return typeof d === 'function';
335
+ var prefix = 'Invariant failed';
336
+ function invariant(condition, message) {
337
+ if (condition) {
338
+ return;
339
+ }
340
+ var provided = typeof message === 'function' ? message() : message;
341
+ var value = provided ? "".concat(prefix, ": ").concat(provided) : prefix;
342
+ throw new Error(value);
377
343
  }
378
- function functionalUpdate(updater, previous) {
379
- if (isFunction(updater)) {
380
- return updater(previous);
344
+
345
+ function warning(condition, message) {
346
+ {
347
+ if (condition) {
348
+ return;
349
+ }
350
+
351
+ var text = "Warning: " + message;
352
+
353
+ if (typeof console !== 'undefined') {
354
+ console.warn(text);
355
+ }
356
+
357
+ try {
358
+ throw Error(text);
359
+ } catch (x) {}
381
360
  }
382
- return updater;
383
- }
384
- function pick(parent, keys) {
385
- return keys.reduce((obj, key) => {
386
- obj[key] = parent[key];
387
- return obj;
388
- }, {});
389
361
  }
390
362
 
363
+ var withSelector = {exports: {}};
364
+
365
+ var withSelector_development = {};
366
+
391
367
  /**
392
- * This function returns `a` if `b` is deeply equal.
393
- * If not, it will replace any deeply equal children of `b` with those of `a`.
394
- * This can be used for structural sharing between immutable JSON values for example.
395
- * Do not use this with signals
368
+ * @license React
369
+ * use-sync-external-store-shim/with-selector.development.js
370
+ *
371
+ * Copyright (c) Facebook, Inc. and its affiliates.
372
+ *
373
+ * This source code is licensed under the MIT license found in the
374
+ * LICENSE file in the root directory of this source tree.
396
375
  */
397
- function replaceEqualDeep(prev, _next) {
398
- if (prev === _next) {
399
- return prev;
400
- }
401
- const next = _next;
402
- const array = Array.isArray(prev) && Array.isArray(next);
403
- if (array || isPlainObject(prev) && isPlainObject(next)) {
404
- const prevSize = array ? prev.length : Object.keys(prev).length;
405
- const nextItems = array ? next : Object.keys(next);
406
- const nextSize = nextItems.length;
407
- const copy = array ? [] : {};
408
- let equalItems = 0;
409
- for (let i = 0; i < nextSize; i++) {
410
- const key = array ? i : nextItems[i];
411
- copy[key] = replaceEqualDeep(prev[key], next[key]);
412
- if (copy[key] === prev[key]) {
413
- equalItems++;
414
- }
415
- }
416
- return prevSize === nextSize && equalItems === prevSize ? prev : copy;
417
- }
418
- return next;
419
- }
420
376
 
421
- // Copied from: https://github.com/jonschlinkert/is-plain-object
422
- function isPlainObject(o) {
423
- if (!hasObjectPrototype(o)) {
424
- return false;
377
+ var hasRequiredWithSelector_development;
378
+
379
+ function requireWithSelector_development () {
380
+ if (hasRequiredWithSelector_development) return withSelector_development;
381
+ hasRequiredWithSelector_development = 1;
382
+
383
+ {
384
+ (function() {
385
+
386
+ /* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */
387
+ if (
388
+ typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' &&
389
+ typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart ===
390
+ 'function'
391
+ ) {
392
+ __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(new Error());
393
+ }
394
+ var React$1 = React;
395
+ var shim = require$$1;
396
+
397
+ /**
398
+ * inlined Object.is polyfill to avoid requiring consumers ship their own
399
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
400
+ */
401
+ function is(x, y) {
402
+ return x === y && (x !== 0 || 1 / x === 1 / y) || x !== x && y !== y // eslint-disable-line no-self-compare
403
+ ;
404
+ }
405
+
406
+ var objectIs = typeof Object.is === 'function' ? Object.is : is;
407
+
408
+ var useSyncExternalStore = shim.useSyncExternalStore;
409
+
410
+ // for CommonJS interop.
411
+
412
+ var useRef = React$1.useRef,
413
+ useEffect = React$1.useEffect,
414
+ useMemo = React$1.useMemo,
415
+ useDebugValue = React$1.useDebugValue; // Same as useSyncExternalStore, but supports selector and isEqual arguments.
416
+
417
+ function useSyncExternalStoreWithSelector(subscribe, getSnapshot, getServerSnapshot, selector, isEqual) {
418
+ // Use this to track the rendered snapshot.
419
+ var instRef = useRef(null);
420
+ var inst;
421
+
422
+ if (instRef.current === null) {
423
+ inst = {
424
+ hasValue: false,
425
+ value: null
426
+ };
427
+ instRef.current = inst;
428
+ } else {
429
+ inst = instRef.current;
430
+ }
431
+
432
+ var _useMemo = useMemo(function () {
433
+ // Track the memoized state using closure variables that are local to this
434
+ // memoized instance of a getSnapshot function. Intentionally not using a
435
+ // useRef hook, because that state would be shared across all concurrent
436
+ // copies of the hook/component.
437
+ var hasMemo = false;
438
+ var memoizedSnapshot;
439
+ var memoizedSelection;
440
+
441
+ var memoizedSelector = function (nextSnapshot) {
442
+ if (!hasMemo) {
443
+ // The first time the hook is called, there is no memoized result.
444
+ hasMemo = true;
445
+ memoizedSnapshot = nextSnapshot;
446
+
447
+ var _nextSelection = selector(nextSnapshot);
448
+
449
+ if (isEqual !== undefined) {
450
+ // Even if the selector has changed, the currently rendered selection
451
+ // may be equal to the new selection. We should attempt to reuse the
452
+ // current value if possible, to preserve downstream memoizations.
453
+ if (inst.hasValue) {
454
+ var currentSelection = inst.value;
455
+
456
+ if (isEqual(currentSelection, _nextSelection)) {
457
+ memoizedSelection = currentSelection;
458
+ return currentSelection;
459
+ }
460
+ }
461
+ }
462
+
463
+ memoizedSelection = _nextSelection;
464
+ return _nextSelection;
465
+ } // We may be able to reuse the previous invocation's result.
466
+
467
+
468
+ // We may be able to reuse the previous invocation's result.
469
+ var prevSnapshot = memoizedSnapshot;
470
+ var prevSelection = memoizedSelection;
471
+
472
+ if (objectIs(prevSnapshot, nextSnapshot)) {
473
+ // The snapshot is the same as last time. Reuse the previous selection.
474
+ return prevSelection;
475
+ } // The snapshot has changed, so we need to compute a new selection.
476
+
477
+
478
+ // The snapshot has changed, so we need to compute a new selection.
479
+ var nextSelection = selector(nextSnapshot); // If a custom isEqual function is provided, use that to check if the data
480
+ // has changed. If it hasn't, return the previous selection. That signals
481
+ // to React that the selections are conceptually equal, and we can bail
482
+ // out of rendering.
483
+
484
+ // If a custom isEqual function is provided, use that to check if the data
485
+ // has changed. If it hasn't, return the previous selection. That signals
486
+ // to React that the selections are conceptually equal, and we can bail
487
+ // out of rendering.
488
+ if (isEqual !== undefined && isEqual(prevSelection, nextSelection)) {
489
+ return prevSelection;
490
+ }
491
+
492
+ memoizedSnapshot = nextSnapshot;
493
+ memoizedSelection = nextSelection;
494
+ return nextSelection;
495
+ }; // Assigning this to a constant so that Flow knows it can't change.
496
+
497
+
498
+ // Assigning this to a constant so that Flow knows it can't change.
499
+ var maybeGetServerSnapshot = getServerSnapshot === undefined ? null : getServerSnapshot;
500
+
501
+ var getSnapshotWithSelector = function () {
502
+ return memoizedSelector(getSnapshot());
503
+ };
504
+
505
+ var getServerSnapshotWithSelector = maybeGetServerSnapshot === null ? undefined : function () {
506
+ return memoizedSelector(maybeGetServerSnapshot());
507
+ };
508
+ return [getSnapshotWithSelector, getServerSnapshotWithSelector];
509
+ }, [getSnapshot, getServerSnapshot, selector, isEqual]),
510
+ getSelection = _useMemo[0],
511
+ getServerSelection = _useMemo[1];
512
+
513
+ var value = useSyncExternalStore(subscribe, getSelection, getServerSelection);
514
+ useEffect(function () {
515
+ inst.hasValue = true;
516
+ inst.value = value;
517
+ }, [value]);
518
+ useDebugValue(value);
519
+ return value;
520
+ }
521
+
522
+ withSelector_development.useSyncExternalStoreWithSelector = useSyncExternalStoreWithSelector;
523
+ /* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */
524
+ if (
525
+ typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' &&
526
+ typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop ===
527
+ 'function'
528
+ ) {
529
+ __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(new Error());
530
+ }
531
+
532
+ })();
533
+ }
534
+ return withSelector_development;
535
+ }
536
+
537
+ {
538
+ withSelector.exports = requireWithSelector_development();
539
+ }
540
+
541
+ var withSelectorExports = withSelector.exports;
542
+
543
+ // src/index.ts
544
+ var Store = class {
545
+ constructor(initialState, options) {
546
+ this.listeners = /* @__PURE__ */ new Set();
547
+ this._batching = false;
548
+ this._flushing = 0;
549
+ this._nextPriority = null;
550
+ this.subscribe = (listener) => {
551
+ this.listeners.add(listener);
552
+ const unsub = this.options?.onSubscribe?.(listener, this);
553
+ return () => {
554
+ this.listeners.delete(listener);
555
+ unsub?.();
556
+ };
557
+ };
558
+ this.setState = (updater, opts) => {
559
+ const previous = this.state;
560
+ this.state = this.options?.updateFn ? this.options.updateFn(previous)(updater) : updater(previous);
561
+ const priority = opts?.priority ?? this.options?.defaultPriority ?? "high";
562
+ if (this._nextPriority === null) {
563
+ this._nextPriority = priority;
564
+ } else if (this._nextPriority === "high") {
565
+ this._nextPriority = priority;
566
+ } else {
567
+ this._nextPriority = this.options?.defaultPriority ?? "high";
568
+ }
569
+ this.options?.onUpdate?.({
570
+ priority: this._nextPriority
571
+ });
572
+ this._flush();
573
+ };
574
+ this._flush = () => {
575
+ if (this._batching)
576
+ return;
577
+ const flushId = ++this._flushing;
578
+ this.listeners.forEach((listener) => {
579
+ if (this._flushing !== flushId)
580
+ return;
581
+ listener({
582
+ priority: this._nextPriority ?? "high"
583
+ });
584
+ });
585
+ };
586
+ this.batch = (cb) => {
587
+ if (this._batching)
588
+ return cb();
589
+ this._batching = true;
590
+ cb();
591
+ this._batching = false;
592
+ this._flush();
593
+ };
594
+ this.state = initialState;
595
+ this.options = options;
425
596
  }
597
+ };
426
598
 
427
- // If has modified constructor
428
- const ctor = o.constructor;
429
- if (typeof ctor === 'undefined') {
599
+ // src/index.ts
600
+ function useStore(store, selector = (d) => d) {
601
+ const slice = withSelectorExports.useSyncExternalStoreWithSelector(
602
+ store.subscribe,
603
+ () => store.state,
604
+ () => store.state,
605
+ selector,
606
+ shallow$1
607
+ );
608
+ return slice;
609
+ }
610
+ function shallow$1(objA, objB) {
611
+ if (Object.is(objA, objB)) {
430
612
  return true;
431
613
  }
432
-
433
- // If has modified prototype
434
- const prot = ctor.prototype;
435
- if (!hasObjectPrototype(prot)) {
614
+ if (typeof objA !== "object" || objA === null || typeof objB !== "object" || objB === null) {
436
615
  return false;
437
616
  }
438
-
439
- // If constructor does not have an Object-specific method
440
- if (!prot.hasOwnProperty('isPrototypeOf')) {
617
+ const keysA = Object.keys(objA);
618
+ if (keysA.length !== Object.keys(objB).length) {
441
619
  return false;
442
620
  }
443
-
444
- // Most likely a plain Object
445
- return true;
446
- }
621
+ for (let i = 0; i < keysA.length; i++) {
622
+ if (!Object.prototype.hasOwnProperty.call(objB, keysA[i]) || !Object.is(objA[keysA[i]], objB[keysA[i]])) {
623
+ return false;
624
+ }
625
+ }
626
+ return true;
627
+ }
628
+
629
+ function CatchBoundary(props) {
630
+ const errorComponent = props.errorComponent ?? ErrorComponent;
631
+ return /*#__PURE__*/React__namespace.createElement(CatchBoundaryImpl, {
632
+ getResetKey: props.getResetKey,
633
+ onCatch: props.onCatch,
634
+ children: ({
635
+ error
636
+ }) => {
637
+ if (error) {
638
+ return /*#__PURE__*/React__namespace.createElement(errorComponent, {
639
+ error
640
+ });
641
+ }
642
+ return props.children;
643
+ }
644
+ });
645
+ }
646
+ class CatchBoundaryImpl extends React__namespace.Component {
647
+ state = {
648
+ error: null
649
+ };
650
+ static getDerivedStateFromProps(props) {
651
+ return {
652
+ resetKey: props.getResetKey()
653
+ };
654
+ }
655
+ static getDerivedStateFromError(error) {
656
+ return {
657
+ error
658
+ };
659
+ }
660
+ componentDidUpdate(prevProps, prevState) {
661
+ if (prevState.error && prevState.resetKey !== this.state.resetKey) {
662
+ this.setState({
663
+ error: null
664
+ });
665
+ }
666
+ }
667
+ componentDidCatch(error) {
668
+ console.error(error);
669
+ this.props.onCatch?.(error);
670
+ }
671
+ render() {
672
+ return this.props.children(this.state);
673
+ }
674
+ }
675
+ function ErrorComponent({
676
+ error
677
+ }) {
678
+ const [show, setShow] = React__namespace.useState("development" !== 'production');
679
+ return /*#__PURE__*/React__namespace.createElement("div", {
680
+ style: {
681
+ padding: '.5rem',
682
+ maxWidth: '100%'
683
+ }
684
+ }, /*#__PURE__*/React__namespace.createElement("div", {
685
+ style: {
686
+ display: 'flex',
687
+ alignItems: 'center',
688
+ gap: '.5rem'
689
+ }
690
+ }, /*#__PURE__*/React__namespace.createElement("strong", {
691
+ style: {
692
+ fontSize: '1rem'
693
+ }
694
+ }, "Something went wrong!"), /*#__PURE__*/React__namespace.createElement("button", {
695
+ style: {
696
+ appearance: 'none',
697
+ fontSize: '.6em',
698
+ border: '1px solid currentColor',
699
+ padding: '.1rem .2rem',
700
+ fontWeight: 'bold',
701
+ borderRadius: '.25rem'
702
+ },
703
+ onClick: () => setShow(d => !d)
704
+ }, show ? 'Hide Error' : 'Show Error')), /*#__PURE__*/React__namespace.createElement("div", {
705
+ style: {
706
+ height: '.25rem'
707
+ }
708
+ }), show ? /*#__PURE__*/React__namespace.createElement("div", null, /*#__PURE__*/React__namespace.createElement("pre", {
709
+ style: {
710
+ fontSize: '.7em',
711
+ border: '1px solid red',
712
+ borderRadius: '.25rem',
713
+ padding: '.3rem',
714
+ color: 'red',
715
+ overflow: 'auto'
716
+ }
717
+ }, error.message ? /*#__PURE__*/React__namespace.createElement("code", null, error.message) : null)) : null);
718
+ }
719
+
720
+ // export type Expand<T> = T
721
+
722
+ // type Compute<T> = { [K in keyof T]: T[K] } | never
723
+
724
+ // type AllKeys<T> = T extends any ? keyof T : never
725
+
726
+ // export type MergeUnion<T, Keys extends keyof T = keyof T> = Compute<
727
+ // {
728
+ // [K in Keys]: T[Keys]
729
+ // } & {
730
+ // [K in AllKeys<T>]?: T extends any
731
+ // ? K extends keyof T
732
+ // ? T[K]
733
+ // : never
734
+ // : never
735
+ // }
736
+ // >
737
+
738
+ // // Sample types to merge
739
+ // type TypeA = {
740
+ // shared: string
741
+ // onlyInA: string
742
+ // nested: {
743
+ // shared: string
744
+ // aProp: string
745
+ // }
746
+ // array: string[]
747
+ // }
748
+
749
+ // type TypeB = {
750
+ // shared: number
751
+ // onlyInB: number
752
+ // nested: {
753
+ // shared: number
754
+ // bProp: number
755
+ // }
756
+ // array: number[]
757
+ // }
758
+
759
+ // type TypeC = {
760
+ // shared: boolean
761
+ // onlyInC: boolean
762
+ // nested: {
763
+ // shared: boolean
764
+ // cProp: boolean
765
+ // }
766
+ // array: boolean[]
767
+ // }
768
+
769
+ // type Test = Expand<Assign<TypeA, TypeB>>
770
+
771
+ // // Using DeepMerge to merge TypeA and TypeB
772
+ // type MergedType = Expand<AssignAll<[TypeA, TypeB, TypeC]>>
773
+
774
+ // from https://github.com/type-challenges/type-challenges/issues/737
775
+
776
+ //
777
+
778
+ const isServer = typeof document === 'undefined';
779
+ function last(arr) {
780
+ return arr[arr.length - 1];
781
+ }
782
+ function isFunction(d) {
783
+ return typeof d === 'function';
784
+ }
785
+ function functionalUpdate(updater, previous) {
786
+ if (isFunction(updater)) {
787
+ return updater(previous);
788
+ }
789
+ return updater;
790
+ }
791
+ function pick(parent, keys) {
792
+ return keys.reduce((obj, key) => {
793
+ obj[key] = parent[key];
794
+ return obj;
795
+ }, {});
796
+ }
797
+
798
+ /**
799
+ * This function returns `a` if `b` is deeply equal.
800
+ * If not, it will replace any deeply equal children of `b` with those of `a`.
801
+ * This can be used for structural sharing between immutable JSON values for example.
802
+ * Do not use this with signals
803
+ */
804
+ function replaceEqualDeep(prev, _next) {
805
+ if (prev === _next) {
806
+ return prev;
807
+ }
808
+ const next = _next;
809
+ const array = Array.isArray(prev) && Array.isArray(next);
810
+ if (array || isPlainObject(prev) && isPlainObject(next)) {
811
+ const prevSize = array ? prev.length : Object.keys(prev).length;
812
+ const nextItems = array ? next : Object.keys(next);
813
+ const nextSize = nextItems.length;
814
+ const copy = array ? [] : {};
815
+ let equalItems = 0;
816
+ for (let i = 0; i < nextSize; i++) {
817
+ const key = array ? i : nextItems[i];
818
+ copy[key] = replaceEqualDeep(prev[key], next[key]);
819
+ if (copy[key] === prev[key]) {
820
+ equalItems++;
821
+ }
822
+ }
823
+ return prevSize === nextSize && equalItems === prevSize ? prev : copy;
824
+ }
825
+ return next;
826
+ }
827
+
828
+ // Copied from: https://github.com/jonschlinkert/is-plain-object
829
+ function isPlainObject(o) {
830
+ if (!hasObjectPrototype(o)) {
831
+ return false;
832
+ }
833
+
834
+ // If has modified constructor
835
+ const ctor = o.constructor;
836
+ if (typeof ctor === 'undefined') {
837
+ return true;
838
+ }
839
+
840
+ // If has modified prototype
841
+ const prot = ctor.prototype;
842
+ if (!hasObjectPrototype(prot)) {
843
+ return false;
844
+ }
845
+
846
+ // If constructor does not have an Object-specific method
847
+ if (!prot.hasOwnProperty('isPrototypeOf')) {
848
+ return false;
849
+ }
850
+
851
+ // Most likely a plain Object
852
+ return true;
853
+ }
447
854
  function hasObjectPrototype(o) {
448
855
  return Object.prototype.toString.call(o) === '[object Object]';
449
856
  }
450
- function partialDeepEqual(a, b) {
857
+ function deepEqual(a, b, partial = false) {
451
858
  if (a === b) {
452
859
  return true;
453
860
  }
454
861
  if (typeof a !== typeof b) {
455
862
  return false;
456
863
  }
457
- if (isPlainObject(a) && isPlainObject(b)) {
458
- return !Object.keys(b).some(key => !partialDeepEqual(a[key], b[key]));
864
+ if (isPlainObject(a) && isPlainObject(b)) {
865
+ const aKeys = Object.keys(a);
866
+ const bKeys = Object.keys(b);
867
+ if (!partial && aKeys.length !== bKeys.length) {
868
+ return false;
869
+ }
870
+ return !bKeys.some(key => !(key in a) || !deepEqual(a[key], b[key], partial));
871
+ }
872
+ if (Array.isArray(a) && Array.isArray(b)) {
873
+ return !a.some((item, index) => !deepEqual(item, b[index], partial));
874
+ }
875
+ return false;
876
+ }
877
+ function useStableCallback(fn) {
878
+ const fnRef = React__namespace.useRef(fn);
879
+ fnRef.current = fn;
880
+ const ref = React__namespace.useRef((...args) => fnRef.current(...args));
881
+ return ref.current;
882
+ }
883
+ function shallow(objA, objB) {
884
+ if (Object.is(objA, objB)) {
885
+ return true;
886
+ }
887
+ if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
888
+ return false;
889
+ }
890
+ const keysA = Object.keys(objA);
891
+ if (keysA.length !== Object.keys(objB).length) {
892
+ return false;
893
+ }
894
+ for (let i = 0; i < keysA.length; i++) {
895
+ if (!Object.prototype.hasOwnProperty.call(objB, keysA[i]) || !Object.is(objA[keysA[i]], objB[keysA[i]])) {
896
+ return false;
897
+ }
898
+ }
899
+ return true;
900
+ }
901
+ function useRouteContext(opts) {
902
+ return useMatch({
903
+ ...opts,
904
+ select: match => opts?.select ? opts.select(match.context) : match.context
905
+ });
906
+ }
907
+ const useLayoutEffect$1 = typeof window !== 'undefined' ? React__namespace.useLayoutEffect : React__namespace.useEffect;
908
+ function escapeJSON(jsonString) {
909
+ return jsonString.replace(/\\/g, '\\\\') // Escape backslashes
910
+ .replace(/'/g, "\\'") // Escape single quotes
911
+ .replace(/"/g, '\\"'); // Escape double quotes
912
+ }
913
+
914
+ const matchContext = /*#__PURE__*/React__namespace.createContext(undefined);
915
+ function Matches() {
916
+ const router = useRouter();
917
+ const matchId = useRouterState({
918
+ select: s => {
919
+ return getRenderedMatches(s)[0]?.id;
920
+ }
921
+ });
922
+ return /*#__PURE__*/React__namespace.createElement(matchContext.Provider, {
923
+ value: matchId
924
+ }, /*#__PURE__*/React__namespace.createElement(CatchBoundary, {
925
+ getResetKey: () => router.state.resolvedLocation.state?.key,
926
+ errorComponent: ErrorComponent,
927
+ onCatch: () => {
928
+ warning(false, `Error in router! Consider setting an 'errorComponent' in your RootRoute! 👍`);
929
+ }
930
+ }, matchId ? /*#__PURE__*/React__namespace.createElement(Match, {
931
+ matchId: matchId
932
+ }) : null));
933
+ }
934
+ function SafeFragment(props) {
935
+ return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, props.children);
936
+ }
937
+ function Match({
938
+ matchId
939
+ }) {
940
+ const router = useRouter();
941
+ const routeId = useRouterState({
942
+ select: s => getRenderedMatches(s).find(d => d.id === matchId)?.routeId
943
+ });
944
+ invariant(routeId, `Could not find routeId for matchId "${matchId}". Please file an issue!`);
945
+ const route = router.routesById[routeId];
946
+ const PendingComponent = route.options.pendingComponent ?? router.options.defaultPendingComponent;
947
+ const pendingElement = PendingComponent ? /*#__PURE__*/React__namespace.createElement(PendingComponent, null) : null;
948
+ const routeErrorComponent = route.options.errorComponent ?? router.options.defaultErrorComponent ?? ErrorComponent;
949
+ const ResolvedSuspenseBoundary = route.options.wrapInSuspense ?? PendingComponent ?? route.options.component?.preload ?? route.options.pendingComponent?.preload ?? route.options.errorComponent?.preload ? React__namespace.Suspense : SafeFragment;
950
+ const ResolvedCatchBoundary = routeErrorComponent ? CatchBoundary : SafeFragment;
951
+ return /*#__PURE__*/React__namespace.createElement(matchContext.Provider, {
952
+ value: matchId
953
+ }, /*#__PURE__*/React__namespace.createElement(ResolvedSuspenseBoundary, {
954
+ fallback: pendingElement
955
+ }, /*#__PURE__*/React__namespace.createElement(ResolvedCatchBoundary, {
956
+ getResetKey: () => router.state.resolvedLocation.state?.key,
957
+ errorComponent: routeErrorComponent,
958
+ onCatch: () => {
959
+ warning(false, `Error in route match: ${matchId}`);
960
+ }
961
+ }, /*#__PURE__*/React__namespace.createElement(MatchInner, {
962
+ matchId: matchId,
963
+ pendingElement: pendingElement
964
+ }))));
965
+ }
966
+ function MatchInner({
967
+ matchId,
968
+ pendingElement
969
+ }) {
970
+ const router = useRouter();
971
+ const routeId = useRouterState({
972
+ select: s => getRenderedMatches(s).find(d => d.id === matchId)?.routeId
973
+ });
974
+ const route = router.routesById[routeId];
975
+ const match = useRouterState({
976
+ select: s => pick(getRenderedMatches(s).find(d => d.id === matchId), ['status', 'error', 'showPending', 'loadPromise'])
977
+ });
978
+ if (match.status === 'error') {
979
+ throw match.error;
980
+ }
981
+ if (match.status === 'pending') {
982
+ if (match.showPending) {
983
+ return pendingElement;
984
+ }
985
+ throw match.loadPromise;
986
+ }
987
+ if (match.status === 'success') {
988
+ let Comp = route.options.component ?? router.options.defaultComponent;
989
+ if (Comp) {
990
+ return /*#__PURE__*/React__namespace.createElement(Comp, null);
991
+ }
992
+ return /*#__PURE__*/React__namespace.createElement(Outlet, null);
993
+ }
994
+ invariant(false, 'Idle routeMatch status encountered during rendering! You should never see this. File an issue!');
995
+ }
996
+ const Outlet = /*#__PURE__*/React__namespace.memo(function Outlet() {
997
+ const matchId = React__namespace.useContext(matchContext);
998
+ const childMatchId = useRouterState({
999
+ select: s => {
1000
+ const matches = getRenderedMatches(s);
1001
+ const index = matches.findIndex(d => d.id === matchId);
1002
+ return matches[index + 1]?.id;
1003
+ }
1004
+ });
1005
+ if (!childMatchId) {
1006
+ return null;
1007
+ }
1008
+ return /*#__PURE__*/React__namespace.createElement(Match, {
1009
+ matchId: childMatchId
1010
+ });
1011
+ });
1012
+ function useMatchRoute() {
1013
+ useRouterState({
1014
+ select: s => [s.location, s.resolvedLocation]
1015
+ });
1016
+ const {
1017
+ matchRoute
1018
+ } = useRouter();
1019
+ return React__namespace.useCallback(opts => {
1020
+ const {
1021
+ pending,
1022
+ caseSensitive,
1023
+ ...rest
1024
+ } = opts;
1025
+ return matchRoute(rest, {
1026
+ pending,
1027
+ caseSensitive
1028
+ });
1029
+ }, []);
1030
+ }
1031
+ function MatchRoute(props) {
1032
+ const matchRoute = useMatchRoute();
1033
+ const params = matchRoute(props);
1034
+ if (typeof props.children === 'function') {
1035
+ return props.children(params);
1036
+ }
1037
+ return !!params ? props.children : null;
1038
+ }
1039
+ function getRenderedMatches(state) {
1040
+ return state.pendingMatches?.some(d => d.showPending) ? state.pendingMatches : state.matches;
1041
+ }
1042
+ function useMatch(opts) {
1043
+ const router = useRouter();
1044
+ const nearestMatchId = React__namespace.useContext(matchContext);
1045
+ const nearestMatchRouteId = getRenderedMatches(router.state).find(d => d.id === nearestMatchId)?.routeId;
1046
+ const matchRouteId = (() => {
1047
+ const matches = getRenderedMatches(router.state);
1048
+ const match = opts?.from ? matches.find(d => d.routeId === opts?.from) : matches.find(d => d.id === nearestMatchId);
1049
+ return match.routeId;
1050
+ })();
1051
+ if (opts?.strict ?? true) {
1052
+ 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?`);
1053
+ }
1054
+ const matchSelection = useRouterState({
1055
+ select: state => {
1056
+ const match = getRenderedMatches(state).find(d => d.id === nearestMatchId);
1057
+ invariant(match, `Could not find ${opts?.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`);
1058
+ return opts?.select ? opts.select(match) : match;
1059
+ }
1060
+ });
1061
+ return matchSelection;
1062
+ }
1063
+ function useMatches(opts) {
1064
+ return useRouterState({
1065
+ select: state => {
1066
+ let matches = getRenderedMatches(state);
1067
+ return opts?.select ? opts.select(matches) : matches;
1068
+ }
1069
+ });
1070
+ }
1071
+ function useParentMatches(opts) {
1072
+ const contextMatchId = React__namespace.useContext(matchContext);
1073
+ return useMatches({
1074
+ select: matches => {
1075
+ matches = matches.slice(matches.findIndex(d => d.id === contextMatchId));
1076
+ return opts?.select ? opts.select(matches) : matches;
1077
+ }
1078
+ });
1079
+ }
1080
+ function useLoaderDeps(opts) {
1081
+ return useMatch({
1082
+ ...opts,
1083
+ select: s => {
1084
+ return typeof opts.select === 'function' ? opts.select(s?.loaderDeps) : s?.loaderDeps;
1085
+ }
1086
+ });
1087
+ }
1088
+ function useLoaderData(opts) {
1089
+ return useMatch({
1090
+ ...opts,
1091
+ select: s => {
1092
+ return typeof opts.select === 'function' ? opts.select(s?.loaderData) : s?.loaderData;
1093
+ }
1094
+ });
1095
+ }
1096
+
1097
+ exports.routerContext = /*#__PURE__*/React__namespace.createContext(null);
1098
+ if (typeof document !== 'undefined') {
1099
+ if (window.__TSR_ROUTER_CONTEXT__) {
1100
+ exports.routerContext = window.__TSR_ROUTER_CONTEXT__;
1101
+ } else {
1102
+ window.__TSR_ROUTER_CONTEXT__ = exports.routerContext;
1103
+ }
1104
+ }
1105
+ function RouterProvider({
1106
+ router,
1107
+ ...rest
1108
+ }) {
1109
+ // Allow the router to update options on the router instance
1110
+ router.update({
1111
+ ...router.options,
1112
+ ...rest,
1113
+ context: {
1114
+ ...router.options.context,
1115
+ ...rest?.context
1116
+ }
1117
+ });
1118
+ const matches = router.options.InnerWrap ? /*#__PURE__*/React__namespace.createElement(router.options.InnerWrap, null, /*#__PURE__*/React__namespace.createElement(Matches, null)) : /*#__PURE__*/React__namespace.createElement(Matches, null);
1119
+ const provider = /*#__PURE__*/React__namespace.createElement(exports.routerContext.Provider, {
1120
+ value: router
1121
+ }, matches, /*#__PURE__*/React__namespace.createElement(Transitioner, null));
1122
+ if (router.options.Wrap) {
1123
+ return /*#__PURE__*/React__namespace.createElement(router.options.Wrap, null, provider);
1124
+ }
1125
+ return provider;
1126
+ }
1127
+ function Transitioner() {
1128
+ const router = useRouter();
1129
+ const routerState = useRouterState({
1130
+ select: s => pick(s, ['isLoading', 'location', 'resolvedLocation', 'isTransitioning'])
1131
+ });
1132
+ const [isTransitioning, startReactTransition] = React__namespace.useTransition();
1133
+ router.startReactTransition = startReactTransition;
1134
+ React__namespace.useEffect(() => {
1135
+ if (isTransitioning) {
1136
+ router.__store.setState(s => ({
1137
+ ...s,
1138
+ isTransitioning
1139
+ }));
1140
+ }
1141
+ }, [isTransitioning]);
1142
+ const tryLoad = () => {
1143
+ const apply = cb => {
1144
+ if (!routerState.isTransitioning) {
1145
+ startReactTransition(() => cb());
1146
+ } else {
1147
+ cb();
1148
+ }
1149
+ };
1150
+ apply(() => {
1151
+ try {
1152
+ router.load();
1153
+ } catch (err) {
1154
+ console.error(err);
1155
+ }
1156
+ });
1157
+ };
1158
+ useLayoutEffect$1(() => {
1159
+ const unsub = router.history.subscribe(() => {
1160
+ router.latestLocation = router.parseLocation(router.latestLocation);
1161
+ if (routerState.location !== router.latestLocation) {
1162
+ tryLoad();
1163
+ }
1164
+ });
1165
+ const nextLocation = router.buildLocation({
1166
+ search: true,
1167
+ params: true,
1168
+ hash: true,
1169
+ state: true
1170
+ });
1171
+ if (routerState.location.href !== nextLocation.href) {
1172
+ router.commitLocation({
1173
+ ...nextLocation,
1174
+ replace: true
1175
+ });
1176
+ }
1177
+ return () => {
1178
+ unsub();
1179
+ };
1180
+ }, [router.history]);
1181
+ useLayoutEffect$1(() => {
1182
+ if (routerState.isTransitioning && !isTransitioning && !routerState.isLoading && routerState.resolvedLocation !== routerState.location) {
1183
+ router.emit({
1184
+ type: 'onResolved',
1185
+ fromLocation: routerState.resolvedLocation,
1186
+ toLocation: routerState.location,
1187
+ pathChanged: routerState.location.href !== routerState.resolvedLocation?.href
1188
+ });
1189
+ if (document.querySelector) {
1190
+ if (routerState.location.hash !== '') {
1191
+ const el = document.getElementById(routerState.location.hash);
1192
+ if (el) {
1193
+ el.scrollIntoView();
1194
+ }
1195
+ }
1196
+ }
1197
+ router.__store.setState(s => ({
1198
+ ...s,
1199
+ isTransitioning: false,
1200
+ resolvedLocation: s.location
1201
+ }));
1202
+ }
1203
+ }, [routerState.isTransitioning, isTransitioning, routerState.isLoading, routerState.resolvedLocation, routerState.location]);
1204
+ useLayoutEffect$1(() => {
1205
+ if (!window.__TSR_DEHYDRATED__) {
1206
+ tryLoad();
1207
+ }
1208
+ }, []);
1209
+ return null;
1210
+ }
1211
+ function getRouteMatch(state, id) {
1212
+ return [...state.cachedMatches, ...(state.pendingMatches ?? []), ...state.matches].find(d => d.id === id);
1213
+ }
1214
+ function useRouterState(opts) {
1215
+ const router = useRouter();
1216
+ return useStore(router.__store, opts?.select);
1217
+ }
1218
+ function useRouter() {
1219
+ const resolvedContext = typeof document !== 'undefined' ? window.__TSR_ROUTER_CONTEXT__ || exports.routerContext : exports.routerContext;
1220
+ const value = React__namespace.useContext(resolvedContext);
1221
+ warning(value, 'useRouter must be used inside a <RouterProvider> component!');
1222
+ return value;
1223
+ }
1224
+
1225
+ function defer(_promise) {
1226
+ const promise = _promise;
1227
+ if (!promise.__deferredState) {
1228
+ promise.__deferredState = {
1229
+ uid: Math.random().toString(36).slice(2),
1230
+ status: 'pending'
1231
+ };
1232
+ const state = promise.__deferredState;
1233
+ promise.then(data => {
1234
+ state.status = 'success';
1235
+ state.data = data;
1236
+ }).catch(error => {
1237
+ state.status = 'error';
1238
+ state.error = error;
1239
+ });
1240
+ }
1241
+ return promise;
1242
+ }
1243
+ function isDehydratedDeferred(obj) {
1244
+ return typeof obj === 'object' && obj !== null && !(obj instanceof Promise) && !obj.then && '__deferredState' in obj;
1245
+ }
1246
+
1247
+ function useAwaited({
1248
+ promise
1249
+ }) {
1250
+ const router = useRouter();
1251
+ let state = promise.__deferredState;
1252
+ const key = `__TSR__DEFERRED__${state.uid}`;
1253
+ if (isDehydratedDeferred(promise)) {
1254
+ state = router.hydrateData(key);
1255
+ promise = Promise.resolve(state.data);
1256
+ promise.__deferredState = state;
1257
+ }
1258
+ if (state.status === 'pending') {
1259
+ throw new Promise(r => setTimeout(r, 1)).then(() => promise);
459
1260
  }
460
- if (Array.isArray(a) && Array.isArray(b)) {
461
- return a.length === b.length && a.every((item, index) => partialDeepEqual(item, b[index]));
1261
+ if (state.status === 'error') {
1262
+ throw state.error;
462
1263
  }
463
- return false;
1264
+ router.dehydrateData(key, state);
1265
+ return [state.data];
1266
+ }
1267
+ function Await(props) {
1268
+ const awaited = useAwaited(props);
1269
+ return props.children(...awaited);
464
1270
  }
465
1271
 
466
1272
  function joinPaths(paths) {
@@ -554,45 +1360,59 @@
554
1360
  }
555
1361
  return segments;
556
1362
  }
557
- function interpolatePath(path, params, leaveWildcard) {
1363
+ function interpolatePath(path, params, leaveWildcards = false) {
558
1364
  const interpolatedPathSegments = parsePathname(path);
559
1365
  return joinPaths(interpolatedPathSegments.map(segment => {
560
- if (['$', '*'].includes(segment.value) && !leaveWildcard) {
561
- return '';
1366
+ if (segment.type === 'wildcard') {
1367
+ const value = params[segment.value];
1368
+ if (leaveWildcards) return `${segment.value}${value ?? ''}`;
1369
+ return value;
562
1370
  }
563
1371
  if (segment.type === 'param') {
564
- return params[segment.value.substring(1)] ?? '';
1372
+ return params[segment.value.substring(1)] ?? 'undefined';
565
1373
  }
566
1374
  return segment.value;
567
1375
  }));
568
1376
  }
569
1377
  function matchPathname(basepath, currentPathname, matchLocation) {
570
1378
  const pathParams = matchByPath(basepath, currentPathname, matchLocation);
571
- // const searchMatched = matchBySearch(currentLocation.search, matchLocation)
1379
+ // const searchMatched = matchBySearch(location.search, matchLocation)
572
1380
 
573
1381
  if (matchLocation.to && !pathParams) {
574
1382
  return;
575
1383
  }
576
1384
  return pathParams ?? {};
577
1385
  }
1386
+ function removeBasepath(basepath, pathname) {
1387
+ return basepath != '/' ? pathname.substring(basepath.length) : pathname;
1388
+ }
578
1389
  function matchByPath(basepath, from, matchLocation) {
579
- if (!from.startsWith(basepath)) {
580
- return undefined;
581
- }
582
- from = basepath != '/' ? from.substring(basepath.length) : from;
583
- const baseSegments = parsePathname(from);
1390
+ // Remove the base path from the pathname
1391
+ from = removeBasepath(basepath, from);
1392
+ // Default to to $ (wildcard)
584
1393
  const to = `${matchLocation.to ?? '$'}`;
1394
+ // Parse the from and to
1395
+ const baseSegments = parsePathname(from);
585
1396
  const routeSegments = parsePathname(to);
586
- if (last(baseSegments)?.value === '/') {
587
- baseSegments.pop();
1397
+ if (!from.startsWith('/')) {
1398
+ baseSegments.unshift({
1399
+ type: 'pathname',
1400
+ value: '/'
1401
+ });
1402
+ }
1403
+ if (!to.startsWith('/')) {
1404
+ routeSegments.unshift({
1405
+ type: 'pathname',
1406
+ value: '/'
1407
+ });
588
1408
  }
589
1409
  const params = {};
590
1410
  let isMatch = (() => {
591
1411
  for (let i = 0; i < Math.max(baseSegments.length, routeSegments.length); i++) {
592
1412
  const baseSegment = baseSegments[i];
593
1413
  const routeSegment = routeSegments[i];
594
- const isLastRouteSegment = i === routeSegments.length - 1;
595
- const isLastBaseSegment = i === baseSegments.length - 1;
1414
+ const isLastBaseSegment = i >= baseSegments.length - 1;
1415
+ const isLastRouteSegment = i >= routeSegments.length - 1;
596
1416
  if (routeSegment) {
597
1417
  if (routeSegment.type === 'wildcard') {
598
1418
  if (baseSegment?.value) {
@@ -627,8 +1447,9 @@
627
1447
  }
628
1448
  }
629
1449
  }
630
- if (isLastRouteSegment && !isLastBaseSegment) {
631
- return !!matchLocation.fuzzy;
1450
+ if (!isLastBaseSegment && isLastRouteSegment) {
1451
+ params['**'] = joinPaths(baseSegments.slice(i + 1).map(d => d.value));
1452
+ return !!matchLocation.fuzzy && routeSegment?.value !== '/';
632
1453
  }
633
1454
  }
634
1455
  return true;
@@ -636,6 +1457,417 @@
636
1457
  return isMatch ? params : undefined;
637
1458
  }
638
1459
 
1460
+ function useParams(opts) {
1461
+ return useRouterState({
1462
+ select: state => {
1463
+ const params = last(state.matches)?.params;
1464
+ return opts?.select ? opts.select(params) : params;
1465
+ }
1466
+ });
1467
+ }
1468
+
1469
+ function useSearch(opts) {
1470
+ return useMatch({
1471
+ ...opts,
1472
+ select: match => {
1473
+ return opts?.select ? opts.select(match.search) : match.search;
1474
+ }
1475
+ });
1476
+ }
1477
+
1478
+ const rootRouteId = '__root__';
1479
+
1480
+ // The parse type here allows a zod schema to be passed directly to the validator
1481
+
1482
+ class RouteApi {
1483
+ constructor({
1484
+ id
1485
+ }) {
1486
+ this.id = id;
1487
+ }
1488
+ useMatch = opts => {
1489
+ return useMatch({
1490
+ ...opts,
1491
+ from: this.id
1492
+ });
1493
+ };
1494
+ useRouteContext = opts => {
1495
+ return useMatch({
1496
+ ...opts,
1497
+ from: this.id,
1498
+ select: d => opts?.select ? opts.select(d.context) : d.context
1499
+ });
1500
+ };
1501
+ useSearch = opts => {
1502
+ return useSearch({
1503
+ ...opts,
1504
+ from: this.id
1505
+ });
1506
+ };
1507
+ useParams = opts => {
1508
+ return useParams({
1509
+ ...opts,
1510
+ from: this.id
1511
+ });
1512
+ };
1513
+ useLoaderDeps = opts => {
1514
+ return useLoaderDeps({
1515
+ ...opts,
1516
+ from: this.id
1517
+ });
1518
+ };
1519
+ useLoaderData = opts => {
1520
+ return useLoaderData({
1521
+ ...opts,
1522
+ from: this.id
1523
+ });
1524
+ };
1525
+ }
1526
+ class Route {
1527
+ // Set up in this.init()
1528
+
1529
+ // customId!: TCustomId
1530
+
1531
+ // Optional
1532
+
1533
+ constructor(options) {
1534
+ this.options = options || {};
1535
+ this.isRoot = !options?.getParentRoute;
1536
+ invariant(!(options?.id && options?.path), `Route cannot have both an 'id' and a 'path' option.`);
1537
+ this.$$typeof = Symbol.for('react.memo');
1538
+ }
1539
+ init = opts => {
1540
+ this.originalIndex = opts.originalIndex;
1541
+ const options = this.options;
1542
+ const isRoot = !options?.path && !options?.id;
1543
+ this.parentRoute = this.options?.getParentRoute?.();
1544
+ if (isRoot) {
1545
+ this.path = rootRouteId;
1546
+ } else {
1547
+ invariant(this.parentRoute, `Child Route instances must pass a 'getParentRoute: () => ParentRoute' option that returns a Route instance.`);
1548
+ }
1549
+ let path = isRoot ? rootRouteId : options.path;
1550
+
1551
+ // If the path is anything other than an index path, trim it up
1552
+ if (path && path !== '/') {
1553
+ path = trimPath(path);
1554
+ }
1555
+ const customId = options?.id || path;
1556
+
1557
+ // Strip the parentId prefix from the first level of children
1558
+ let id = isRoot ? rootRouteId : joinPaths([this.parentRoute.id === rootRouteId ? '' : this.parentRoute.id, customId]);
1559
+ if (path === rootRouteId) {
1560
+ path = '/';
1561
+ }
1562
+ if (id !== rootRouteId) {
1563
+ id = joinPaths(['/', id]);
1564
+ }
1565
+ const fullPath = id === rootRouteId ? '/' : joinPaths([this.parentRoute.fullPath, path]);
1566
+ this.path = path;
1567
+ this.id = id;
1568
+ // this.customId = customId as TCustomId
1569
+ this.fullPath = fullPath;
1570
+ this.to = fullPath;
1571
+ };
1572
+ addChildren = children => {
1573
+ this.children = children;
1574
+ return this;
1575
+ };
1576
+ update = options => {
1577
+ Object.assign(this.options, options);
1578
+ return this;
1579
+ };
1580
+ useMatch = opts => {
1581
+ return useMatch({
1582
+ ...opts,
1583
+ from: this.id
1584
+ });
1585
+ };
1586
+ useRouteContext = opts => {
1587
+ return useMatch({
1588
+ ...opts,
1589
+ from: this.id,
1590
+ select: d => opts?.select ? opts.select(d.context) : d.context
1591
+ });
1592
+ };
1593
+ useSearch = opts => {
1594
+ return useSearch({
1595
+ ...opts,
1596
+ from: this.id
1597
+ });
1598
+ };
1599
+ useParams = opts => {
1600
+ return useParams({
1601
+ ...opts,
1602
+ from: this.id
1603
+ });
1604
+ };
1605
+ useLoaderDeps = opts => {
1606
+ return useLoaderDeps({
1607
+ ...opts,
1608
+ from: this.id
1609
+ });
1610
+ };
1611
+ useLoaderData = opts => {
1612
+ return useLoaderData({
1613
+ ...opts,
1614
+ from: this.id
1615
+ });
1616
+ };
1617
+ }
1618
+ function rootRouteWithContext() {
1619
+ return options => {
1620
+ return new RootRoute(options);
1621
+ };
1622
+ }
1623
+ class RootRoute extends Route {
1624
+ constructor(options) {
1625
+ super(options);
1626
+ }
1627
+ }
1628
+ function createRouteMask(opts) {
1629
+ return opts;
1630
+ }
1631
+
1632
+ //
1633
+
1634
+ class NotFoundRoute extends Route {
1635
+ constructor(options) {
1636
+ super({
1637
+ ...options,
1638
+ id: '404'
1639
+ });
1640
+ }
1641
+ }
1642
+
1643
+ class FileRoute {
1644
+ constructor(path) {
1645
+ this.path = path;
1646
+ }
1647
+ createRoute = options => {
1648
+ const route = new Route(options);
1649
+ route.isRoot = false;
1650
+ return route;
1651
+ };
1652
+ }
1653
+
1654
+ function lazyRouteComponent(importer, exportName) {
1655
+ let loadPromise;
1656
+ const load = () => {
1657
+ if (!loadPromise) {
1658
+ loadPromise = importer();
1659
+ }
1660
+ return loadPromise;
1661
+ };
1662
+ const lazyComp = /*#__PURE__*/React__namespace.lazy(async () => {
1663
+ const moduleExports = await load();
1664
+ const comp = moduleExports[exportName ?? 'default'];
1665
+ return {
1666
+ default: comp
1667
+ };
1668
+ });
1669
+ lazyComp.preload = load;
1670
+ return lazyComp;
1671
+ }
1672
+
1673
+ function _extends() {
1674
+ _extends = Object.assign ? Object.assign.bind() : function (target) {
1675
+ for (var i = 1; i < arguments.length; i++) {
1676
+ var source = arguments[i];
1677
+ for (var key in source) {
1678
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
1679
+ target[key] = source[key];
1680
+ }
1681
+ }
1682
+ }
1683
+ return target;
1684
+ };
1685
+ return _extends.apply(this, arguments);
1686
+ }
1687
+
1688
+ const preloadWarning = 'Error preloading route! ☝️';
1689
+ function useLinkProps(options) {
1690
+ const router = useRouter();
1691
+ const matchPathname = useMatch({
1692
+ strict: false,
1693
+ select: s => s.pathname
1694
+ });
1695
+ const {
1696
+ // custom props
1697
+ children,
1698
+ target,
1699
+ activeProps = () => ({
1700
+ className: 'active'
1701
+ }),
1702
+ inactiveProps = () => ({}),
1703
+ activeOptions,
1704
+ disabled,
1705
+ hash,
1706
+ search,
1707
+ params,
1708
+ to,
1709
+ state,
1710
+ mask,
1711
+ preload: userPreload,
1712
+ preloadDelay: userPreloadDelay,
1713
+ replace,
1714
+ startTransition,
1715
+ resetScroll,
1716
+ // element props
1717
+ style,
1718
+ className,
1719
+ onClick,
1720
+ onFocus,
1721
+ onMouseEnter,
1722
+ onMouseLeave,
1723
+ onTouchStart,
1724
+ ...rest
1725
+ } = options;
1726
+
1727
+ // If this link simply reloads the current route,
1728
+ // make sure it has a new key so it will trigger a data refresh
1729
+
1730
+ // If this `to` is a valid external URL, return
1731
+ // null for LinkUtils
1732
+
1733
+ const dest = {
1734
+ from: options.to ? matchPathname : undefined,
1735
+ ...options
1736
+ };
1737
+ let type = 'internal';
1738
+ try {
1739
+ new URL(`${to}`);
1740
+ type = 'external';
1741
+ } catch {}
1742
+ if (type === 'external') {
1743
+ return {
1744
+ href: to
1745
+ };
1746
+ }
1747
+ const next = router.buildLocation(dest);
1748
+ const preload = userPreload ?? router.options.defaultPreload;
1749
+ const preloadDelay = userPreloadDelay ?? router.options.defaultPreloadDelay ?? 0;
1750
+ const isActive = useRouterState({
1751
+ select: s => {
1752
+ // Compare path/hash for matches
1753
+ const currentPathSplit = s.location.pathname.split('/');
1754
+ const nextPathSplit = next.pathname.split('/');
1755
+ const pathIsFuzzyEqual = nextPathSplit.every((d, i) => d === currentPathSplit[i]);
1756
+ // Combine the matches based on user router.options
1757
+ const pathTest = activeOptions?.exact ? s.location.pathname === next.pathname : pathIsFuzzyEqual;
1758
+ const hashTest = activeOptions?.includeHash ? s.location.hash === next.hash : true;
1759
+ const searchTest = activeOptions?.includeSearch ?? true ? deepEqual(s.location.search, next.search, !activeOptions?.exact) : true;
1760
+
1761
+ // The final "active" test
1762
+ return pathTest && hashTest && searchTest;
1763
+ }
1764
+ });
1765
+
1766
+ // The click handler
1767
+ const handleClick = e => {
1768
+ if (!disabled && !isCtrlEvent(e) && !e.defaultPrevented && (!target || target === '_self') && e.button === 0) {
1769
+ e.preventDefault();
1770
+
1771
+ // All is well? Navigate!
1772
+ router.commitLocation({
1773
+ ...next,
1774
+ replace,
1775
+ resetScroll,
1776
+ startTransition
1777
+ });
1778
+ }
1779
+ };
1780
+
1781
+ // The click handler
1782
+ const handleFocus = e => {
1783
+ if (preload) {
1784
+ router.preloadRoute(dest).catch(err => {
1785
+ console.warn(err);
1786
+ console.warn(preloadWarning);
1787
+ });
1788
+ }
1789
+ };
1790
+ const handleTouchStart = e => {
1791
+ if (preload) {
1792
+ router.preloadRoute(dest).catch(err => {
1793
+ console.warn(err);
1794
+ console.warn(preloadWarning);
1795
+ });
1796
+ }
1797
+ };
1798
+ const handleEnter = e => {
1799
+ const target = e.target || {};
1800
+ if (preload) {
1801
+ if (target.preloadTimeout) {
1802
+ return;
1803
+ }
1804
+ target.preloadTimeout = setTimeout(() => {
1805
+ target.preloadTimeout = null;
1806
+ router.preloadRoute(dest).catch(err => {
1807
+ console.warn(err);
1808
+ console.warn(preloadWarning);
1809
+ });
1810
+ }, preloadDelay);
1811
+ }
1812
+ };
1813
+ const handleLeave = e => {
1814
+ const target = e.target || {};
1815
+ if (target.preloadTimeout) {
1816
+ clearTimeout(target.preloadTimeout);
1817
+ target.preloadTimeout = null;
1818
+ }
1819
+ };
1820
+ const composeHandlers = handlers => e => {
1821
+ if (e.persist) e.persist();
1822
+ handlers.filter(Boolean).forEach(handler => {
1823
+ if (e.defaultPrevented) return;
1824
+ handler(e);
1825
+ });
1826
+ };
1827
+
1828
+ // Get the active props
1829
+ const resolvedActiveProps = isActive ? functionalUpdate(activeProps, {}) ?? {} : {};
1830
+
1831
+ // Get the inactive props
1832
+ const resolvedInactiveProps = isActive ? {} : functionalUpdate(inactiveProps, {}) ?? {};
1833
+ return {
1834
+ ...resolvedActiveProps,
1835
+ ...resolvedInactiveProps,
1836
+ ...rest,
1837
+ href: disabled ? undefined : next.maskedLocation ? next.maskedLocation.href : next.href,
1838
+ onClick: composeHandlers([onClick, handleClick]),
1839
+ onFocus: composeHandlers([onFocus, handleFocus]),
1840
+ onMouseEnter: composeHandlers([onMouseEnter, handleEnter]),
1841
+ onMouseLeave: composeHandlers([onMouseLeave, handleLeave]),
1842
+ onTouchStart: composeHandlers([onTouchStart, handleTouchStart]),
1843
+ target,
1844
+ style: {
1845
+ ...style,
1846
+ ...resolvedActiveProps.style,
1847
+ ...resolvedInactiveProps.style
1848
+ },
1849
+ className: [className, resolvedActiveProps.className, resolvedInactiveProps.className].filter(Boolean).join(' ') || undefined,
1850
+ ...(disabled ? {
1851
+ role: 'link',
1852
+ 'aria-disabled': true
1853
+ } : undefined),
1854
+ ['data-status']: isActive ? 'active' : undefined
1855
+ };
1856
+ }
1857
+ const Link = /*#__PURE__*/React__namespace.forwardRef((props, ref) => {
1858
+ const linkProps = useLinkProps(props);
1859
+ return /*#__PURE__*/React__namespace.createElement("a", _extends({
1860
+ ref: ref
1861
+ }, linkProps, {
1862
+ children: typeof props.children === 'function' ? props.children({
1863
+ isActive: linkProps['data-status'] === 'active'
1864
+ }) : props.children
1865
+ }));
1866
+ });
1867
+ function isCtrlEvent(e) {
1868
+ return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
1869
+ }
1870
+
639
1871
  // @ts-nocheck
640
1872
 
641
1873
  // 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.
@@ -665,8 +1897,7 @@
665
1897
  var str = decodeURIComponent(mix);
666
1898
  if (str === 'false') return false;
667
1899
  if (str === 'true') return true;
668
- if (str.charAt(0) === '0') return str;
669
- return +str * 0 === 0 ? +str : str;
1900
+ return +str * 0 === 0 && +str + '' === str ? +str : str;
670
1901
  }
671
1902
  function decode(str) {
672
1903
  var tmp,
@@ -685,1650 +1916,1452 @@
685
1916
  return out;
686
1917
  }
687
1918
 
688
- const rootRouteId = '__root__';
689
- class Route {
690
- // Set up in this.init()
691
-
692
- // customId!: TCustomId
693
-
694
- // Optional
695
-
696
- constructor(options) {
697
- this.options = options || {};
698
- this.isRoot = !options?.getParentRoute;
699
- }
700
- init = opts => {
701
- this.originalIndex = opts.originalIndex;
702
- this.router = opts.router;
703
- const allOptions = this.options;
704
- const isRoot = !allOptions?.path && !allOptions?.id;
705
- this.parentRoute = this.options?.getParentRoute?.();
706
- if (isRoot) {
707
- this.path = rootRouteId;
708
- } else {
709
- invariant(this.parentRoute, `Child Route instances must pass a 'getParentRoute: () => ParentRoute' option that returns a Route instance.`);
710
- }
711
- let path = isRoot ? rootRouteId : allOptions.path;
712
-
713
- // If the path is anything other than an index path, trim it up
714
- if (path && path !== '/') {
715
- path = trimPath(path);
716
- }
717
- const customId = allOptions?.id || path;
718
-
719
- // Strip the parentId prefix from the first level of children
720
- let id = isRoot ? rootRouteId : joinPaths([this.parentRoute.id === rootRouteId ? '' : this.parentRoute.id, customId]);
721
- if (path === rootRouteId) {
722
- path = '/';
723
- }
724
- if (id !== rootRouteId) {
725
- id = joinPaths(['/', id]);
726
- }
727
- const fullPath = id === rootRouteId ? '/' : trimPathRight(joinPaths([this.parentRoute.fullPath, path]));
728
- this.path = path;
729
- this.id = id;
730
- // this.customId = customId as TCustomId
731
- this.fullPath = fullPath;
732
- };
733
- addChildren = children => {
734
- this.children = children;
735
- return this;
736
- };
737
-
738
- // generate = (
739
- // options: Omit<
740
- // RouteOptions<
741
- // TParentRoute,
742
- // TCustomId,
743
- // TPath,
744
- // InferFullSearchSchema<TParentRoute>,
745
- // TSearchSchema,
746
- // TFullSearchSchema,
747
- // TParentRoute['__types']['allParams'],
748
- // TParams,
749
- // TAllParams,
750
- // TParentContext,
751
- // TAllParentContext,
752
- // TRouteContext,
753
- // TContext
754
- // >,
755
- // 'path'
756
- // >,
757
- // ) => {
758
- // invariant(
759
- // false,
760
- // `route.generate() is used by TanStack Router's file-based routing code generation and should not actually be called during runtime. `,
761
- // )
762
- // }
763
- }
1919
+ // Detect if we're in the DOM
764
1920
 
765
- class RootRoute extends Route {
766
- constructor(options) {
767
- super(options);
1921
+ function redirect(opts) {
1922
+ opts.isRedirect = true;
1923
+ if (opts.throw) {
1924
+ throw opts;
768
1925
  }
769
- static withRouterContext = () => {
770
- return options => new RootRoute(options);
771
- };
772
- }
773
-
774
- // const rootRoute = new RootRoute({
775
- // validateSearch: () => null as unknown as { root?: boolean },
776
- // })
777
-
778
- // const aRoute = new Route({
779
- // getParentRoute: () => rootRoute,
780
- // path: 'a',
781
- // validateSearch: () => null as unknown as { a?: string },
782
- // })
783
-
784
- // const bRoute = new Route({
785
- // getParentRoute: () => aRoute,
786
- // path: 'b',
787
- // })
788
-
789
- // const rootIsRoot = rootRoute.isRoot
790
- // // ^?
791
- // const aIsRoot = aRoute.isRoot
792
- // // ^?
793
-
794
- // const rId = rootRoute.id
795
- // // ^?
796
- // const aId = aRoute.id
797
- // // ^?
798
- // const bId = bRoute.id
799
- // // ^?
800
-
801
- // const rPath = rootRoute.fullPath
802
- // // ^?
803
- // const aPath = aRoute.fullPath
804
- // // ^?
805
- // const bPath = bRoute.fullPath
806
- // // ^?
807
-
808
- // const rSearch = rootRoute.__types.fullSearchSchema
809
- // // ^?
810
- // const aSearch = aRoute.__types.fullSearchSchema
811
- // // ^?
812
- // const bSearch = bRoute.__types.fullSearchSchema
813
- // // ^?
814
-
815
- // const config = rootRoute.addChildren([aRoute.addChildren([bRoute])])
816
- // // ^?
817
-
818
- const defaultParseSearch = parseSearchWith(JSON.parse);
819
- const defaultStringifySearch = stringifySearchWith(JSON.stringify);
820
- function parseSearchWith(parser) {
821
- return searchStr => {
822
- if (searchStr.substring(0, 1) === '?') {
823
- searchStr = searchStr.substring(1);
824
- }
825
- let query = decode(searchStr);
826
-
827
- // Try to parse any query params that might be json
828
- for (let key in query) {
829
- const value = query[key];
830
- if (typeof value === 'string') {
831
- try {
832
- query[key] = parser(value);
833
- } catch (err) {
834
- //
835
- }
836
- }
837
- }
838
- return query;
839
- };
1926
+ return opts;
840
1927
  }
841
- function stringifySearchWith(stringify) {
842
- return search => {
843
- search = {
844
- ...search
845
- };
846
- if (search) {
847
- Object.keys(search).forEach(key => {
848
- const val = search[key];
849
- if (typeof val === 'undefined' || val === undefined) {
850
- delete search[key];
851
- } else if (val && typeof val === 'object' && val !== null) {
852
- try {
853
- search[key] = stringify(val);
854
- } catch (err) {
855
- // silent
856
- }
857
- }
858
- });
859
- }
860
- const searchStr = encode(search).toString();
861
- return searchStr ? `?${searchStr}` : '';
862
- };
1928
+ function isRedirect(obj) {
1929
+ return !!obj?.isRedirect;
863
1930
  }
864
1931
 
865
- const defaultFetchServerDataFn = async ({
866
- router,
867
- routeMatch
868
- }) => {
869
- const next = router.buildNext({
870
- to: '.',
871
- search: d => ({
872
- ...(d ?? {}),
873
- __data: {
874
- matchId: routeMatch.id
875
- }
876
- })
877
- });
878
- const res = await fetch(next.href, {
879
- method: 'GET',
880
- signal: routeMatch.abortController.signal
881
- });
882
- if (res.ok) {
883
- return res.json();
884
- }
885
- throw new Error('Failed to fetch match data');
886
- };
887
- class Router {
888
- #unsubHistory;
889
- startedLoadingAt = Date.now();
890
- resolveNavigation = () => {};
891
- constructor(options) {
892
- this.options = {
893
- defaultPreloadDelay: 50,
894
- context: undefined,
895
- ...options,
896
- stringifySearch: options?.stringifySearch ?? defaultStringifySearch,
897
- parseSearch: options?.parseSearch ?? defaultParseSearch,
898
- fetchServerDataFn: options?.fetchServerDataFn ?? defaultFetchServerDataFn
899
- };
900
- this.__store = new Store(getInitialRouterState(), {
901
- onUpdate: state => {
902
- this.state = state;
903
- }
904
- });
905
- this.state = this.__store.state;
906
- this.basepath = '';
907
- this.update(options);
908
-
909
- // Allow frameworks to hook into the router creation
910
- this.options.Router?.(this);
911
- const next = this.buildNext({
912
- hash: true,
913
- fromCurrent: true,
914
- search: true,
915
- state: true
916
- });
917
- if (this.state.latestLocation.href !== next.href) {
918
- this.#commitLocation({
919
- ...next,
920
- replace: true
921
- });
922
- }
923
- }
924
- reset = () => {
925
- this.__store.setState(s => Object.assign(s, getInitialRouterState()));
926
- };
927
- mount = () => {
928
- // Mount only does anything on the client
929
- if (!isServer) {
930
- // If the router matches are empty, start loading the matches
931
- if (!this.state.currentMatches.length) {
932
- this.safeLoad();
933
- }
934
- }
935
- return () => {};
936
- };
937
- update = opts => {
938
- Object.assign(this.options, opts);
939
- if (!this.history || this.options.history && this.options.history !== this.history) {
940
- if (this.#unsubHistory) {
941
- this.#unsubHistory();
942
- }
943
- this.history = this.options.history ?? (isServer ? createMemoryHistory() : createBrowserHistory());
944
- const parsedLocation = this.#parseLocation();
945
- this.__store.setState(s => ({
946
- ...s,
947
- latestLocation: parsedLocation,
948
- currentLocation: parsedLocation
949
- }));
950
- this.#unsubHistory = this.history.listen(() => {
951
- this.safeLoad({
952
- next: this.#parseLocation(this.state.latestLocation)
953
- });
954
- });
955
- }
956
- const {
957
- basepath,
958
- routeTree
959
- } = this.options;
960
- this.basepath = `/${trimPath(basepath ?? '') ?? ''}`;
961
- if (routeTree) {
962
- this.routesById = {};
963
- this.routeTree = this.#buildRouteTree(routeTree);
964
- }
965
- return this;
966
- };
967
- buildNext = opts => {
968
- const next = this.#buildLocation(opts);
969
- const __matches = this.matchRoutes(next.pathname);
970
- return this.#buildLocation({
971
- ...opts,
972
- __matches
973
- });
974
- };
975
- cancelMatches = () => {
976
- [...this.state.currentMatches, ...(this.state.pendingMatches || [])].forEach(match => {
977
- match.cancel();
978
- });
979
- };
980
- safeLoad = opts => {
981
- this.load(opts).catch(err => {
982
- console.warn(err);
983
- invariant(false, 'Encountered an error during router.load()! ☝️.');
984
- });
985
- };
986
- load = async opts => {
987
- let now = Date.now();
988
- const startedAt = now;
989
- this.startedLoadingAt = startedAt;
990
-
991
- // Cancel any pending matches
992
- this.cancelMatches();
993
- let matches;
994
- this.__store.batch(() => {
995
- if (opts?.next) {
996
- // Ingest the new location
997
- this.__store.setState(s => ({
998
- ...s,
999
- latestLocation: opts.next
1000
- }));
1001
- }
1002
-
1003
- // Match the routes
1004
- matches = this.matchRoutes(this.state.latestLocation.pathname, {
1005
- strictParseParams: true
1006
- });
1007
- this.__store.setState(s => ({
1008
- ...s,
1009
- status: 'pending',
1010
- pendingMatches: matches,
1011
- pendingLocation: this.state.latestLocation
1012
- }));
1013
- });
1014
-
1015
- // Load the matches
1016
- await this.loadMatches(matches, this.state.pendingLocation
1017
- // opts
1018
- );
1019
-
1020
- if (this.startedLoadingAt !== startedAt) {
1021
- // Ignore side-effects of outdated side-effects
1022
- return this.navigationPromise;
1023
- }
1024
- const previousMatches = this.state.currentMatches;
1025
- const exiting = [],
1026
- staying = [];
1027
- previousMatches.forEach(d => {
1028
- if (matches.find(dd => dd.id === d.id)) {
1029
- staying.push(d);
1030
- } else {
1031
- exiting.push(d);
1032
- }
1033
- });
1034
- const entering = matches.filter(d => {
1035
- return !previousMatches.find(dd => dd.id === d.id);
1036
- });
1037
- now = Date.now();
1038
- exiting.forEach(d => {
1039
- d.__onExit?.({
1040
- params: d.params,
1041
- search: d.state.routeSearch
1042
- });
1043
-
1044
- // Clear non-loading error states when match leaves
1045
- if (d.state.status === 'error') {
1046
- this.__store.setState(s => ({
1047
- ...s,
1048
- status: 'idle',
1049
- error: undefined
1050
- }));
1051
- }
1052
- });
1053
- staying.forEach(d => {
1054
- d.route.options.onTransition?.({
1055
- params: d.params,
1056
- search: d.state.routeSearch
1057
- });
1058
- });
1059
- entering.forEach(d => {
1060
- d.__onExit = d.route.options.onLoaded?.({
1061
- params: d.params,
1062
- search: d.state.search
1063
- });
1064
- });
1065
- const prevLocation = this.state.currentLocation;
1066
- this.__store.setState(s => ({
1067
- ...s,
1068
- status: 'idle',
1069
- currentLocation: this.state.latestLocation,
1070
- currentMatches: matches,
1071
- pendingLocation: undefined,
1072
- pendingMatches: undefined
1073
- }));
1074
- matches.forEach(match => {
1075
- match.__commit();
1076
- });
1077
- if (prevLocation.href !== this.state.currentLocation.href) {
1078
- this.options.onRouteChange?.();
1079
- }
1080
- this.resolveNavigation();
1081
- };
1082
- getRoute = id => {
1083
- const route = this.routesById[id];
1084
- invariant(route, `Route with id "${id}" not found`);
1085
- return route;
1086
- };
1087
- loadRoute = async (navigateOpts = this.state.latestLocation) => {
1088
- const next = this.buildNext(navigateOpts);
1089
- const matches = this.matchRoutes(next.pathname, {
1090
- strictParseParams: true
1091
- });
1092
- await this.loadMatches(matches, next);
1093
- return matches;
1094
- };
1095
- preloadRoute = async (navigateOpts = this.state.latestLocation) => {
1096
- const next = this.buildNext(navigateOpts);
1097
- const matches = this.matchRoutes(next.pathname, {
1098
- strictParseParams: true
1099
- });
1100
- await this.loadMatches(matches, next, {
1101
- preload: true
1102
- });
1103
- return matches;
1104
- };
1105
- matchRoutes = (pathname, opts) => {
1106
- const matches = [];
1107
- if (!this.routeTree) {
1108
- return matches;
1109
- }
1110
- const existingMatches = [...this.state.currentMatches, ...(this.state.pendingMatches ?? [])];
1111
- const findInRouteTree = async routes => {
1112
- const parentMatch = last(matches);
1113
- let params = parentMatch?.params ?? {};
1114
- const filteredRoutes = this.options.filterRoutes?.(routes) ?? routes;
1115
- let matchingRoutes = [];
1116
- const findMatchInRoutes = (parentRoutes, routes) => {
1117
- routes.some(route => {
1118
- const children = route.children;
1119
- if (!route.path && children?.length) {
1120
- return findMatchInRoutes([...matchingRoutes, route], children);
1121
- }
1122
- const fuzzy = !!(route.path !== '/' || children?.length);
1123
- const matchParams = matchPathname(this.basepath, pathname, {
1124
- to: route.fullPath,
1125
- fuzzy,
1126
- caseSensitive: route.options.caseSensitive ?? this.options.caseSensitive
1127
- });
1128
- if (matchParams) {
1129
- let parsedParams;
1130
- try {
1131
- parsedParams = route.options.parseParams?.(matchParams) ?? matchParams;
1132
- } catch (err) {
1133
- if (opts?.strictParseParams) {
1134
- throw err;
1135
- }
1136
- }
1137
- params = {
1138
- ...params,
1139
- ...parsedParams
1140
- };
1141
- }
1142
- if (!!matchParams) {
1143
- matchingRoutes = [...parentRoutes, route];
1144
- }
1145
- return !!matchingRoutes.length;
1146
- });
1147
- return !!matchingRoutes.length;
1148
- };
1149
- findMatchInRoutes([], filteredRoutes);
1150
- if (!matchingRoutes.length) {
1151
- return;
1152
- }
1153
- matchingRoutes.forEach(foundRoute => {
1154
- const interpolatedPath = interpolatePath(foundRoute.path, params);
1155
- const matchId = interpolatePath(foundRoute.id, params, true);
1156
- const match = existingMatches.find(d => d.id === matchId) || new RouteMatch(this, foundRoute, {
1157
- id: matchId,
1158
- params,
1159
- pathname: joinPaths([this.basepath, interpolatedPath])
1160
- });
1161
- matches.push(match);
1162
- });
1163
- const foundRoute = last(matchingRoutes);
1164
- const foundChildren = foundRoute.children;
1165
- if (foundChildren?.length) {
1166
- findInRouteTree(foundChildren);
1167
- }
1168
- };
1169
- findInRouteTree([this.routeTree]);
1170
- return matches;
1171
- };
1172
- loadMatches = async (resolvedMatches, location, opts) => {
1173
- let firstBadMatchIndex;
1174
-
1175
- // Check each match middleware to see if the route can be accessed
1176
- try {
1177
- await Promise.all(resolvedMatches.map(async (match, index) => {
1178
- try {
1179
- await match.route.options.beforeLoad?.({
1180
- router: this,
1181
- match
1182
- });
1183
- } catch (err) {
1184
- if (isRedirect(err)) {
1185
- throw err;
1186
- }
1187
- firstBadMatchIndex = firstBadMatchIndex ?? index;
1188
- const errorHandler = match.route.options.onBeforeLoadError ?? match.route.options.onError;
1189
- try {
1190
- errorHandler?.(err);
1191
- } catch (errorHandlerErr) {
1192
- if (isRedirect(errorHandlerErr)) {
1193
- throw errorHandlerErr;
1194
- }
1195
- match.__store.setState(s => ({
1196
- ...s,
1197
- error: errorHandlerErr,
1198
- status: 'error',
1199
- updatedAt: Date.now()
1200
- }));
1201
- return;
1202
- }
1203
- match.__store.setState(s => ({
1204
- ...s,
1205
- error: err,
1206
- status: 'error',
1207
- updatedAt: Date.now()
1208
- }));
1209
- }
1210
- }));
1211
- } catch (err) {
1212
- if (isRedirect(err)) {
1213
- if (!opts?.preload) {
1214
- this.navigate(err);
1215
- }
1216
- return;
1217
- }
1218
- throw err; // we should never end up here
1932
+ const defaultParseSearch = parseSearchWith(JSON.parse);
1933
+ const defaultStringifySearch = stringifySearchWith(JSON.stringify, JSON.parse);
1934
+ function parseSearchWith(parser) {
1935
+ return searchStr => {
1936
+ if (searchStr.substring(0, 1) === '?') {
1937
+ searchStr = searchStr.substring(1);
1219
1938
  }
1939
+ let query = decode(searchStr);
1220
1940
 
1221
- const validResolvedMatches = resolvedMatches.slice(0, firstBadMatchIndex);
1222
- const matchPromises = validResolvedMatches.map(async (match, index) => {
1223
- const parentMatch = validResolvedMatches[index - 1];
1224
- match.__load({
1225
- preload: opts?.preload,
1226
- location,
1227
- parentMatch
1228
- });
1229
- await match.__loadPromise;
1230
- if (parentMatch) {
1231
- await parentMatch.__loadPromise;
1941
+ // Try to parse any query params that might be json
1942
+ for (let key in query) {
1943
+ const value = query[key];
1944
+ if (typeof value === 'string') {
1945
+ try {
1946
+ query[key] = parser(value);
1947
+ } catch (err) {
1948
+ //
1949
+ }
1232
1950
  }
1233
- });
1234
- await Promise.all(matchPromises);
1235
- };
1236
- reload = () => {
1237
- this.navigate({
1238
- fromCurrent: true,
1239
- replace: true,
1240
- search: true
1241
- });
1951
+ }
1952
+ return query;
1242
1953
  };
1243
- resolvePath = (from, path) => {
1244
- return resolvePath(this.basepath, from, cleanPath(path));
1954
+ }
1955
+ function stringifySearchWith(stringify, parser) {
1956
+ function stringifyValue(val) {
1957
+ if (typeof val === 'object' && val !== null) {
1958
+ try {
1959
+ return stringify(val);
1960
+ } catch (err) {
1961
+ // silent
1962
+ }
1963
+ } else if (typeof val === 'string' && typeof parser === 'function') {
1964
+ try {
1965
+ // Check if it's a valid parseable string.
1966
+ // If it is, then stringify it again.
1967
+ parser(val);
1968
+ return stringify(val);
1969
+ } catch (err) {
1970
+ // silent
1971
+ }
1972
+ }
1973
+ return val;
1974
+ }
1975
+ return search => {
1976
+ search = {
1977
+ ...search
1978
+ };
1979
+ if (search) {
1980
+ Object.keys(search).forEach(key => {
1981
+ const val = search[key];
1982
+ if (typeof val === 'undefined' || val === undefined) {
1983
+ delete search[key];
1984
+ } else {
1985
+ search[key] = stringifyValue(val);
1986
+ }
1987
+ });
1988
+ }
1989
+ const searchStr = encode(search).toString();
1990
+ return searchStr ? `?${searchStr}` : '';
1245
1991
  };
1246
- navigate = async ({
1247
- from,
1248
- to = '',
1249
- search,
1250
- hash,
1251
- replace,
1252
- params
1253
- }) => {
1254
- // If this link simply reloads the current route,
1255
- // make sure it has a new key so it will trigger a data refresh
1992
+ }
1256
1993
 
1257
- // If this `to` is a valid external URL, return
1258
- // null for LinkUtils
1259
- const toString = String(to);
1260
- const fromString = typeof from === 'undefined' ? from : String(from);
1261
- let isExternal;
1262
- try {
1263
- new URL(`${toString}`);
1264
- isExternal = true;
1265
- } catch (e) {}
1266
- invariant(!isExternal, 'Attempting to navigate to external url with this.navigate!');
1267
- return this.#commitLocation({
1268
- from: fromString,
1269
- to: toString,
1270
- search,
1271
- hash,
1272
- replace,
1273
- params
1994
+ // import warning from 'tiny-warning'
1995
+
1996
+ //
1997
+
1998
+ const componentTypes = ['component', 'errorComponent', 'pendingComponent'];
1999
+ class Router {
2000
+ // Option-independent properties
2001
+ tempLocationKey = `${Math.round(Math.random() * 10000000)}`;
2002
+ resetNextScroll = true;
2003
+ navigateTimeout = null;
2004
+ latestLoadPromise = Promise.resolve();
2005
+ subscribers = new Set();
2006
+ injectedHtml = [];
2007
+
2008
+ // Must build in constructor
2009
+
2010
+ constructor(options) {
2011
+ this.update({
2012
+ defaultPreloadDelay: 50,
2013
+ defaultPendingMs: 1000,
2014
+ defaultPendingMinMs: 500,
2015
+ context: undefined,
2016
+ ...options,
2017
+ stringifySearch: options?.stringifySearch ?? defaultStringifySearch,
2018
+ parseSearch: options?.parseSearch ?? defaultParseSearch
1274
2019
  });
1275
- };
1276
- matchRoute = (location, opts) => {
1277
- location = {
1278
- ...location,
1279
- to: location.to ? this.resolvePath(location.from ?? '', location.to) : undefined
2020
+ }
2021
+
2022
+ // These are default implementations that can optionally be overridden
2023
+ // by the router provider once rendered. We provide these so that the
2024
+ // router can be used in a non-react environment if necessary
2025
+ startReactTransition = fn => fn();
2026
+ update = newOptions => {
2027
+ this.options = {
2028
+ ...this.options,
2029
+ ...newOptions
1280
2030
  };
1281
- const next = this.buildNext(location);
1282
- const baseLocation = opts?.pending ? this.state.pendingLocation : this.state.currentLocation;
1283
- if (!baseLocation) {
1284
- return false;
2031
+ this.basepath = `/${trimPath(newOptions.basepath ?? '') ?? ''}`;
2032
+ if (!this.history || this.options.history && this.options.history !== this.history) {
2033
+ this.history = this.options.history ?? (typeof document !== 'undefined' ? createBrowserHistory() : createMemoryHistory());
2034
+ this.latestLocation = this.parseLocation();
1285
2035
  }
1286
- const match = matchPathname(this.basepath, baseLocation.pathname, {
1287
- ...opts,
1288
- to: next.pathname
1289
- });
1290
- if (!match) {
1291
- return false;
2036
+ if (this.options.routeTree !== this.routeTree) {
2037
+ this.routeTree = this.options.routeTree;
2038
+ this.buildRouteTree();
1292
2039
  }
1293
- if (opts?.includeSearch ?? true) {
1294
- return partialDeepEqual(baseLocation.search, next.search) ? match : false;
2040
+ if (!this.__store) {
2041
+ this.__store = new Store(getInitialRouterState(this.latestLocation), {
2042
+ onUpdate: () => {
2043
+ this.__store.state = {
2044
+ ...this.state,
2045
+ status: this.state.isTransitioning || this.state.isLoading ? 'pending' : 'idle'
2046
+ };
2047
+ }
2048
+ });
1295
2049
  }
1296
- return match;
1297
2050
  };
1298
- buildLink = ({
1299
- from,
1300
- to = '.',
1301
- search,
1302
- params,
1303
- hash,
1304
- target,
1305
- replace,
1306
- activeOptions,
1307
- preload,
1308
- preloadDelay: userPreloadDelay,
1309
- disabled
1310
- }) => {
1311
- // If this link simply reloads the current route,
1312
- // make sure it has a new key so it will trigger a data refresh
1313
-
1314
- // If this `to` is a valid external URL, return
1315
- // null for LinkUtils
1316
-
1317
- try {
1318
- new URL(`${to}`);
1319
- return {
1320
- type: 'external',
1321
- href: to
1322
- };
1323
- } catch (e) {}
1324
- const nextOpts = {
1325
- from,
1326
- to,
1327
- search,
1328
- params,
1329
- hash,
1330
- replace
2051
+ get state() {
2052
+ return this.__store.state;
2053
+ }
2054
+ buildRouteTree = () => {
2055
+ this.routesById = {};
2056
+ this.routesByPath = {};
2057
+ const notFoundRoute = this.options.notFoundRoute;
2058
+ if (notFoundRoute) {
2059
+ notFoundRoute.init({
2060
+ originalIndex: 99999999999
2061
+ });
2062
+ this.routesById[notFoundRoute.id] = notFoundRoute;
2063
+ }
2064
+ const recurseRoutes = childRoutes => {
2065
+ childRoutes.forEach((childRoute, i) => {
2066
+ childRoute.init({
2067
+ originalIndex: i
2068
+ });
2069
+ const existingRoute = this.routesById[childRoute.id];
2070
+ invariant(!existingRoute, `Duplicate routes found with id: ${String(childRoute.id)}`);
2071
+ this.routesById[childRoute.id] = childRoute;
2072
+ if (!childRoute.isRoot && childRoute.path) {
2073
+ const trimmedFullPath = trimPathRight(childRoute.fullPath);
2074
+ if (!this.routesByPath[trimmedFullPath] || childRoute.fullPath.endsWith('/')) {
2075
+ this.routesByPath[trimmedFullPath] = childRoute;
2076
+ }
2077
+ }
2078
+ const children = childRoute.children;
2079
+ if (children?.length) {
2080
+ recurseRoutes(children);
2081
+ }
2082
+ });
1331
2083
  };
1332
- const next = this.buildNext(nextOpts);
1333
- preload = preload ?? this.options.defaultPreload;
1334
- const preloadDelay = userPreloadDelay ?? this.options.defaultPreloadDelay ?? 0;
1335
-
1336
- // Compare path/hash for matches
1337
- const currentPathSplit = this.state.currentLocation.pathname.split('/');
1338
- const nextPathSplit = next.pathname.split('/');
1339
- const pathIsFuzzyEqual = nextPathSplit.every((d, i) => d === currentPathSplit[i]);
1340
- // Combine the matches based on user options
1341
- const pathTest = activeOptions?.exact ? this.state.currentLocation.pathname === next.pathname : pathIsFuzzyEqual;
1342
- const hashTest = activeOptions?.includeHash ? this.state.currentLocation.hash === next.hash : true;
1343
- const searchTest = activeOptions?.includeSearch ?? true ? partialDeepEqual(this.state.currentLocation.search, next.search) : true;
1344
-
1345
- // The final "active" test
1346
- const isActive = pathTest && hashTest && searchTest;
1347
-
1348
- // The click handler
1349
- const handleClick = e => {
1350
- if (!disabled && !isCtrlEvent(e) && !e.defaultPrevented && (!target || target === '_self') && e.button === 0) {
1351
- e.preventDefault();
1352
-
1353
- // All is well? Navigate!
1354
- this.#commitLocation(nextOpts);
2084
+ recurseRoutes([this.routeTree]);
2085
+ const scoredRoutes = [];
2086
+ Object.values(this.routesById).forEach((d, i) => {
2087
+ if (d.isRoot || !d.path) {
2088
+ return;
1355
2089
  }
1356
- };
1357
-
1358
- // The click handler
1359
- const handleFocus = e => {
1360
- if (preload) {
1361
- this.preloadRoute(nextOpts).catch(err => {
1362
- console.warn(err);
1363
- console.warn('Error preloading route! ☝️');
1364
- });
2090
+ const trimmed = trimPathLeft(d.fullPath);
2091
+ const parsed = parsePathname(trimmed);
2092
+ while (parsed.length > 1 && parsed[0]?.value === '/') {
2093
+ parsed.shift();
1365
2094
  }
1366
- };
1367
- const handleTouchStart = e => {
1368
- this.preloadRoute(nextOpts).catch(err => {
1369
- console.warn(err);
1370
- console.warn('Error preloading route! ☝️');
2095
+ const scores = parsed.map(d => {
2096
+ if (d.value === '/') {
2097
+ return 0.75;
2098
+ }
2099
+ if (d.type === 'param') {
2100
+ return 0.5;
2101
+ }
2102
+ if (d.type === 'wildcard') {
2103
+ return 0.25;
2104
+ }
2105
+ return 1;
1371
2106
  });
1372
- };
1373
- const handleEnter = e => {
1374
- const target = e.target || {};
1375
- if (preload) {
1376
- if (target.preloadTimeout) {
1377
- return;
2107
+ scoredRoutes.push({
2108
+ child: d,
2109
+ trimmed,
2110
+ parsed,
2111
+ index: i,
2112
+ scores
2113
+ });
2114
+ });
2115
+ this.flatRoutes = scoredRoutes.sort((a, b) => {
2116
+ const minLength = Math.min(a.scores.length, b.scores.length);
2117
+
2118
+ // Sort by min available score
2119
+ for (let i = 0; i < minLength; i++) {
2120
+ if (a.scores[i] !== b.scores[i]) {
2121
+ return b.scores[i] - a.scores[i];
1378
2122
  }
1379
- target.preloadTimeout = setTimeout(() => {
1380
- target.preloadTimeout = null;
1381
- this.preloadRoute(nextOpts).catch(err => {
1382
- console.warn(err);
1383
- console.warn('Error preloading route! ☝️');
1384
- });
1385
- }, preloadDelay);
1386
2123
  }
1387
- };
1388
- const handleLeave = e => {
1389
- const target = e.target || {};
1390
- if (target.preloadTimeout) {
1391
- clearTimeout(target.preloadTimeout);
1392
- target.preloadTimeout = null;
2124
+
2125
+ // Sort by length of score
2126
+ if (a.scores.length !== b.scores.length) {
2127
+ return b.scores.length - a.scores.length;
2128
+ }
2129
+
2130
+ // Sort by min available parsed value
2131
+ for (let i = 0; i < minLength; i++) {
2132
+ if (a.parsed[i].value !== b.parsed[i].value) {
2133
+ return a.parsed[i].value > b.parsed[i].value ? 1 : -1;
2134
+ }
1393
2135
  }
2136
+
2137
+ // Sort by original index
2138
+ return a.index - b.index;
2139
+ }).map((d, i) => {
2140
+ d.child.rank = i;
2141
+ return d.child;
2142
+ });
2143
+ };
2144
+ subscribe = (eventType, fn) => {
2145
+ const listener = {
2146
+ eventType,
2147
+ fn
1394
2148
  };
1395
- return {
1396
- type: 'internal',
1397
- next,
1398
- handleFocus,
1399
- handleClick,
1400
- handleEnter,
1401
- handleLeave,
1402
- handleTouchStart,
1403
- isActive,
1404
- disabled
2149
+ this.subscribers.add(listener);
2150
+ return () => {
2151
+ this.subscribers.delete(listener);
1405
2152
  };
1406
2153
  };
1407
- dehydrate = () => {
1408
- return {
1409
- state: {
1410
- ...pick(this.state, ['latestLocation', 'currentLocation', 'status', 'lastUpdated']),
1411
- currentMatches: this.state.currentMatches.map(match => ({
1412
- id: match.id,
1413
- state: {
1414
- status: match.state.status
1415
- }
1416
- }))
2154
+ emit = routerEvent => {
2155
+ this.subscribers.forEach(listener => {
2156
+ if (listener.eventType === routerEvent.type) {
2157
+ listener.fn(routerEvent);
1417
2158
  }
1418
- };
1419
- };
1420
- hydrate = dehydratedRouter => {
1421
- this.__store.setState(s => {
1422
- // Match the routes
1423
- const currentMatches = this.matchRoutes(dehydratedRouter.state.latestLocation.pathname, {
1424
- strictParseParams: true
1425
- });
1426
- currentMatches.forEach((match, index) => {
1427
- const dehydratedMatch = dehydratedRouter.state.currentMatches[index];
1428
- invariant(dehydratedMatch && dehydratedMatch.id === match.id, 'Oh no! There was a hydration mismatch when attempting to hydrate the state of the router! 😬');
1429
- match.__store.setState(s => ({
1430
- ...s,
1431
- ...dehydratedMatch.state
1432
- }));
1433
- });
1434
- return {
1435
- ...s,
1436
- ...dehydratedRouter.state,
1437
- currentMatches
1438
- };
1439
2159
  });
1440
2160
  };
1441
- #buildRouteTree = routeTree => {
1442
- const recurseRoutes = (routes, parentRoute) => {
1443
- routes.forEach((route, i) => {
1444
- route.init({
1445
- originalIndex: i,
1446
- router: this
1447
- });
1448
- const existingRoute = this.routesById[route.id];
1449
- invariant(!existingRoute, `Duplicate routes found with id: ${String(route.id)}`);
1450
- this.routesById[route.id] = route;
1451
- const children = route.children;
1452
- if (children?.length) {
1453
- recurseRoutes(children);
1454
- route.children = children.map((d, i) => {
1455
- const parsed = parsePathname(trimPathLeft(cleanPath(d.path ?? '/')));
1456
- while (parsed.length > 1 && parsed[0]?.value === '/') {
1457
- parsed.shift();
1458
- }
1459
- let score = 0;
1460
- parsed.forEach((d, i) => {
1461
- let modifier = 1;
1462
- while (i--) {
1463
- modifier *= 0.001;
1464
- }
1465
- if (d.type === 'pathname' && d.value !== '/') {
1466
- score += 1 * modifier;
1467
- } else if (d.type === 'param') {
1468
- score += 2 * modifier;
1469
- } else if (d.type === 'wildcard') {
1470
- score += 3 * modifier;
1471
- }
1472
- });
1473
- return {
1474
- child: d,
1475
- parsed,
1476
- index: i,
1477
- score
1478
- };
1479
- }).sort((a, b) => {
1480
- if (a.score !== b.score) {
1481
- return a.score - b.score;
1482
- }
1483
- return a.index - b.index;
1484
- }).map(d => d.child);
1485
- }
1486
- });
1487
- };
1488
- recurseRoutes([routeTree]);
1489
- const recurceCheckRoutes = (routes, parentRoute) => {
1490
- routes.forEach(route => {
1491
- if (route.isRoot) {
1492
- invariant(!parentRoute, 'Root routes can only be used as the root of a route tree.');
1493
- } else {
1494
- invariant(parentRoute ? route.parentRoute === parentRoute : true, `Expected a route with path "${route.path}" to be passed to its parent route "${route.parentRoute?.id}" in an addChildren() call, but was instead passed as a child of the "${parentRoute?.id}" route.`);
1495
- }
1496
- if (route.children) {
1497
- recurceCheckRoutes(route.children, route);
1498
- }
1499
- });
1500
- };
1501
- recurceCheckRoutes([routeTree], undefined);
1502
- return routeTree;
2161
+ checkLatest = promise => {
2162
+ return this.latestLoadPromise !== promise ? this.latestLoadPromise : undefined;
1503
2163
  };
1504
- #parseLocation = previousLocation => {
1505
- let {
2164
+ parseLocation = previousLocation => {
2165
+ const parse = ({
1506
2166
  pathname,
1507
2167
  search,
1508
2168
  hash,
1509
2169
  state
1510
- } = this.history.location;
1511
- const parsedSearch = this.options.parseSearch(search);
1512
- return {
1513
- pathname: pathname,
1514
- searchStr: search,
1515
- search: replaceEqualDeep(previousLocation?.search, parsedSearch),
1516
- hash: hash.split('#').reverse()[0] ?? '',
1517
- href: `${pathname}${search}${hash}`,
1518
- state: state,
1519
- key: state?.key || '__init__'
1520
- };
1521
- };
1522
- #buildLocation = (dest = {}) => {
1523
- dest.fromCurrent = dest.fromCurrent ?? dest.to === '';
1524
- const fromPathname = dest.fromCurrent ? this.state.latestLocation.pathname : dest.from ?? this.state.latestLocation.pathname;
1525
- let pathname = resolvePath(this.basepath ?? '/', fromPathname, `${dest.to ?? ''}`);
1526
- const fromMatches = this.matchRoutes(this.state.latestLocation.pathname, {
1527
- strictParseParams: true
1528
- });
1529
- const prevParams = {
1530
- ...last(fromMatches)?.params
2170
+ }) => {
2171
+ const parsedSearch = this.options.parseSearch(search);
2172
+ return {
2173
+ pathname: pathname,
2174
+ searchStr: search,
2175
+ search: replaceEqualDeep(previousLocation?.search, parsedSearch),
2176
+ hash: hash.split('#').reverse()[0] ?? '',
2177
+ href: `${pathname}${search}${hash}`,
2178
+ state: replaceEqualDeep(previousLocation?.state, state)
2179
+ };
1531
2180
  };
1532
- let nextParams = (dest.params ?? true) === true ? prevParams : functionalUpdate(dest.params, prevParams);
1533
- if (nextParams) {
1534
- dest.__matches?.map(d => d.route.options.stringifyParams).filter(Boolean).forEach(fn => {
1535
- nextParams = {
1536
- ...nextParams,
1537
- ...fn(nextParams)
1538
- };
1539
- });
2181
+ const location = parse(this.history.location);
2182
+ let {
2183
+ __tempLocation,
2184
+ __tempKey
2185
+ } = location.state;
2186
+ if (__tempLocation && (!__tempKey || __tempKey === this.tempLocationKey)) {
2187
+ // Sync up the location keys
2188
+ const parsedTempLocation = parse(__tempLocation);
2189
+ parsedTempLocation.state.key = location.state.key;
2190
+ delete parsedTempLocation.state.__tempLocation;
2191
+ return {
2192
+ ...parsedTempLocation,
2193
+ maskedLocation: location
2194
+ };
1540
2195
  }
1541
- pathname = interpolatePath(pathname, nextParams ?? {});
1542
- const preSearchFilters = dest.__matches?.map(match => match.route.options.preSearchFilters ?? []).flat().filter(Boolean) ?? [];
1543
- const postSearchFilters = dest.__matches?.map(match => match.route.options.postSearchFilters ?? []).flat().filter(Boolean) ?? [];
1544
-
1545
- // Pre filters first
1546
- const preFilteredSearch = preSearchFilters?.length ? preSearchFilters?.reduce((prev, next) => next(prev), this.state.latestLocation.search) : this.state.latestLocation.search;
1547
-
1548
- // Then the link/navigate function
1549
- const destSearch = dest.search === true ? preFilteredSearch // Preserve resolvedFrom true
1550
- : dest.search ? functionalUpdate(dest.search, preFilteredSearch) ?? {} // Updater
1551
- : preSearchFilters?.length ? preFilteredSearch // Preserve resolvedFrom filters
1552
- : {};
1553
-
1554
- // Then post filters
1555
- const postFilteredSearch = postSearchFilters?.length ? postSearchFilters.reduce((prev, next) => next(prev), destSearch) : destSearch;
1556
- const search = replaceEqualDeep(this.state.latestLocation.search, postFilteredSearch);
1557
- const searchStr = this.options.stringifySearch(search);
1558
- let hash = dest.hash === true ? this.state.latestLocation.hash : functionalUpdate(dest.hash, this.state.latestLocation.hash);
1559
- hash = hash ? `#${hash}` : '';
1560
- const nextState = dest.state === true ? this.state.latestLocation.state : functionalUpdate(dest.state, this.state.latestLocation.state);
1561
- return {
1562
- pathname,
1563
- search,
1564
- searchStr,
1565
- state: nextState,
1566
- hash,
1567
- href: this.history.createHref(`${pathname}${searchStr}${hash}`),
1568
- key: dest.key
1569
- };
2196
+ return location;
1570
2197
  };
1571
- #commitLocation = async location => {
1572
- const next = this.buildNext(location);
1573
- const id = '' + Date.now() + Math.random();
1574
- if (this.navigateTimeout) clearTimeout(this.navigateTimeout);
1575
- let nextAction = 'replace';
1576
- if (!location.replace) {
1577
- nextAction = 'push';
1578
- }
1579
- const isSameUrl = this.state.latestLocation.href === next.href;
1580
- if (isSameUrl && !next.key) {
1581
- nextAction = 'replace';
1582
- }
1583
- const href = `${next.pathname}${next.searchStr}${next.hash ? `${next.hash}` : ''}`;
1584
- this.history[nextAction === 'push' ? 'push' : 'replace'](href, {
1585
- id,
1586
- ...next.state
1587
- });
1588
- return this.navigationPromise = new Promise(resolve => {
1589
- const previousNavigationResolve = this.resolveNavigation;
1590
- this.resolveNavigation = () => {
1591
- previousNavigationResolve();
1592
- resolve();
1593
- };
1594
- });
2198
+ resolvePathWithBase = (from, path) => {
2199
+ return resolvePath(this.basepath, from, cleanPath(path));
1595
2200
  };
1596
- }
2201
+ get looseRoutesById() {
2202
+ return this.routesById;
2203
+ }
2204
+ matchRoutes = (pathname, locationSearch, opts) => {
2205
+ let routeParams = {};
2206
+ let foundRoute = this.flatRoutes.find(route => {
2207
+ const matchedParams = matchPathname(this.basepath, trimPathRight(pathname), {
2208
+ to: route.fullPath,
2209
+ caseSensitive: route.options.caseSensitive ?? this.options.caseSensitive,
2210
+ fuzzy: true
2211
+ });
2212
+ if (matchedParams) {
2213
+ routeParams = matchedParams;
2214
+ return true;
2215
+ }
2216
+ return false;
2217
+ });
2218
+ let routeCursor = foundRoute || this.routesById['__root__'];
2219
+ let matchedRoutes = [routeCursor];
2220
+
2221
+ // Check to see if the route needs a 404 entry
2222
+ if (
2223
+ // If we found a route, and it's not an index route and we have left over path
2224
+ (foundRoute ? foundRoute.path !== '/' && routeParams['**'] :
2225
+ // Or if we didn't find a route and we have left over path
2226
+ trimPathRight(pathname)) &&
2227
+ // And we have a 404 route configured
2228
+ this.options.notFoundRoute) {
2229
+ matchedRoutes.push(this.options.notFoundRoute);
2230
+ }
2231
+ while (routeCursor?.parentRoute) {
2232
+ routeCursor = routeCursor.parentRoute;
2233
+ if (routeCursor) matchedRoutes.unshift(routeCursor);
2234
+ }
1597
2235
 
1598
- // Detect if we're in the DOM
1599
- const isServer = typeof window === 'undefined' || !window.document.createElement;
1600
- function getInitialRouterState() {
1601
- return {
1602
- status: 'idle',
1603
- latestLocation: null,
1604
- currentLocation: null,
1605
- currentMatches: [],
1606
- lastUpdated: Date.now()
1607
- };
1608
- }
1609
- function isCtrlEvent(e) {
1610
- return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
1611
- }
1612
- function redirect(opts) {
1613
- opts.isRedirect = true;
1614
- return opts;
1615
- }
1616
- function isRedirect(obj) {
1617
- return !!obj?.isRedirect;
1618
- }
2236
+ // Existing matches are matches that are already loaded along with
2237
+ // pending matches that are still loading
1619
2238
 
1620
- const componentTypes = ['component', 'errorComponent', 'pendingComponent'];
1621
- class RouteMatch {
1622
- abortController = new AbortController();
1623
- constructor(router, route, opts) {
1624
- Object.assign(this, {
1625
- route,
1626
- router,
1627
- id: opts.id,
1628
- pathname: opts.pathname,
1629
- params: opts.params,
1630
- __store: new Store({
1631
- updatedAt: 0,
1632
- routeSearch: {},
2239
+ const parseErrors = matchedRoutes.map(route => {
2240
+ let parsedParamsError;
2241
+ if (route.options.parseParams) {
2242
+ try {
2243
+ const parsedParams = route.options.parseParams(routeParams);
2244
+ // Add the parsed params to the accumulated params bag
2245
+ Object.assign(routeParams, parsedParams);
2246
+ } catch (err) {
2247
+ parsedParamsError = new PathParamError(err.message, {
2248
+ cause: err
2249
+ });
2250
+ if (opts?.throwOnError) {
2251
+ throw parsedParamsError;
2252
+ }
2253
+ return parsedParamsError;
2254
+ }
2255
+ }
2256
+ return;
2257
+ });
2258
+ const matches = [];
2259
+ matchedRoutes.forEach((route, index) => {
2260
+ // Take each matched route and resolve + validate its search params
2261
+ // This has to happen serially because each route's search params
2262
+ // can depend on the parent route's search params
2263
+ // It must also happen before we create the match so that we can
2264
+ // pass the search params to the route's potential key function
2265
+ // which is used to uniquely identify the route match in state
2266
+
2267
+ const parentMatch = matches[index - 1];
2268
+ const [preMatchSearch, searchError] = (() => {
2269
+ // Validate the search params and stabilize them
2270
+ const parentSearch = parentMatch?.search ?? locationSearch;
2271
+ try {
2272
+ const validator = typeof route.options.validateSearch === 'object' ? route.options.validateSearch.parse : route.options.validateSearch;
2273
+ let search = validator?.(parentSearch) ?? {};
2274
+ return [{
2275
+ ...parentSearch,
2276
+ ...search
2277
+ }, undefined];
2278
+ } catch (err) {
2279
+ const searchError = new SearchParamError(err.message, {
2280
+ cause: err
2281
+ });
2282
+ if (opts?.throwOnError) {
2283
+ throw searchError;
2284
+ }
2285
+ return [parentSearch, searchError];
2286
+ }
2287
+ })();
2288
+
2289
+ // This is where we need to call route.options.loaderDeps() to get any additional
2290
+ // deps that the route's loader function might need to run. We need to do this
2291
+ // before we create the match so that we can pass the deps to the route's
2292
+ // potential key function which is used to uniquely identify the route match in state
2293
+
2294
+ const loaderDeps = route.options.loaderDeps?.({
2295
+ search: preMatchSearch
2296
+ }) ?? '';
2297
+ const loaderDepsHash = loaderDeps ? JSON.stringify(loaderDeps) : '';
2298
+ const interpolatedPath = interpolatePath(route.fullPath, routeParams);
2299
+ const matchId = interpolatePath(route.id, routeParams, true) + loaderDepsHash;
2300
+
2301
+ // Waste not, want not. If we already have a match for this route,
2302
+ // reuse it. This is important for layout routes, which might stick
2303
+ // around between navigation actions that only change leaf routes.
2304
+ const existingMatch = getRouteMatch(this.state, matchId);
2305
+ const cause = this.state.matches.find(d => d.id === matchId) ? 'stay' : 'enter';
2306
+
2307
+ // Create a fresh route match
2308
+ const hasLoaders = !!(route.options.loader || componentTypes.some(d => route.options[d]?.preload));
2309
+ const match = existingMatch ? {
2310
+ ...existingMatch,
2311
+ cause
2312
+ } : {
2313
+ id: matchId,
2314
+ routeId: route.id,
2315
+ params: routeParams,
2316
+ pathname: joinPaths([this.basepath, interpolatedPath]),
2317
+ updatedAt: Date.now(),
1633
2318
  search: {},
1634
- status: 'idle'
1635
- }, {
1636
- onUpdate: next => {
1637
- this.state = next;
1638
- }
1639
- })
1640
- });
1641
- this.state = this.__store.state;
1642
- componentTypes.map(async type => {
1643
- const component = this.route.options[type];
1644
- if (typeof this[type] !== 'function') {
1645
- this[type] = component;
1646
- }
2319
+ searchError: undefined,
2320
+ status: hasLoaders ? 'pending' : 'success',
2321
+ showPending: false,
2322
+ isFetching: false,
2323
+ error: undefined,
2324
+ paramsError: parseErrors[index],
2325
+ loadPromise: Promise.resolve(),
2326
+ routeContext: undefined,
2327
+ context: undefined,
2328
+ abortController: new AbortController(),
2329
+ fetchCount: 0,
2330
+ cause,
2331
+ loaderDeps,
2332
+ invalid: false,
2333
+ preload: false
2334
+ };
2335
+
2336
+ // Regardless of whether we're reusing an existing match or creating
2337
+ // a new one, we need to update the match's search params
2338
+ match.search = replaceEqualDeep(match.search, preMatchSearch);
2339
+ // And also update the searchError if there is one
2340
+ match.searchError = searchError;
2341
+ matches.push(match);
1647
2342
  });
1648
- if (this.state.status === 'idle' && !this.#hasLoaders()) {
1649
- this.__store.setState(s => ({
1650
- ...s,
1651
- status: 'success'
1652
- }));
1653
- }
1654
- }
1655
- #hasLoaders = () => {
1656
- return !!(this.route.options.onLoad || componentTypes.some(d => this.route.options[d]?.preload));
2343
+ return matches;
1657
2344
  };
1658
- __commit = () => {
1659
- const {
1660
- routeSearch,
1661
- search,
1662
- context,
1663
- routeContext
1664
- } = this.#resolveInfo({
1665
- location: this.router.state.currentLocation
1666
- });
1667
- this.context = context;
1668
- this.routeContext = routeContext;
1669
- this.__store.setState(s => ({
1670
- ...s,
1671
- routeSearch: replaceEqualDeep(s.routeSearch, routeSearch),
1672
- search: replaceEqualDeep(s.search, search)
1673
- }));
2345
+ cancelMatch = id => {
2346
+ getRouteMatch(this.state, id)?.abortController?.abort();
1674
2347
  };
1675
- cancel = () => {
1676
- this.abortController?.abort();
2348
+ cancelMatches = () => {
2349
+ this.state.pendingMatches?.forEach(match => {
2350
+ this.cancelMatch(match.id);
2351
+ });
1677
2352
  };
1678
- #resolveSearchInfo = opts => {
1679
- // Validate the search params and stabilize them
1680
- const parentSearchInfo = this.parentMatch ? this.parentMatch.#resolveSearchInfo(opts) : {
1681
- search: opts.location.search,
1682
- routeSearch: opts.location.search
1683
- };
1684
- try {
1685
- const validator = typeof this.route.options.validateSearch === 'object' ? this.route.options.validateSearch.parse : this.route.options.validateSearch;
1686
- const routeSearch = validator?.(parentSearchInfo.search) ?? {};
1687
- const search = {
1688
- ...parentSearchInfo.search,
1689
- ...routeSearch
2353
+ buildLocation = opts => {
2354
+ const build = (dest = {}, matches) => {
2355
+ const from = this.latestLocation;
2356
+ const fromSearch = (this.state.pendingMatches || this.state.matches).at(-1)?.search || from.search;
2357
+ const fromPathname = dest.from ?? from.pathname;
2358
+ let pathname = this.resolvePathWithBase(fromPathname, `${dest.to ?? ''}`);
2359
+ const fromMatches = this.matchRoutes(fromPathname, fromSearch);
2360
+ const stayingMatches = matches?.filter(d => fromMatches?.find(e => e.routeId === d.routeId));
2361
+ const prevParams = {
2362
+ ...last(fromMatches)?.params
1690
2363
  };
2364
+ let nextParams = (dest.params ?? true) === true ? prevParams : functionalUpdate(dest.params, prevParams);
2365
+ if (nextParams) {
2366
+ matches?.map(d => this.looseRoutesById[d.routeId].options.stringifyParams).filter(Boolean).forEach(fn => {
2367
+ nextParams = {
2368
+ ...nextParams,
2369
+ ...fn(nextParams)
2370
+ };
2371
+ });
2372
+ }
2373
+ pathname = interpolatePath(pathname, nextParams ?? {});
2374
+ const preSearchFilters = stayingMatches?.map(match => this.looseRoutesById[match.routeId].options.preSearchFilters ?? []).flat().filter(Boolean) ?? [];
2375
+ const postSearchFilters = stayingMatches?.map(match => this.looseRoutesById[match.routeId].options.postSearchFilters ?? []).flat().filter(Boolean) ?? [];
2376
+
2377
+ // Pre filters first
2378
+ const preFilteredSearch = preSearchFilters?.length ? preSearchFilters?.reduce((prev, next) => next(prev), fromSearch) : fromSearch;
2379
+
2380
+ // Then the link/navigate function
2381
+ const destSearch = dest.search === true ? preFilteredSearch // Preserve resolvedFrom true
2382
+ : dest.search ? functionalUpdate(dest.search, preFilteredSearch) ?? {} // Updater
2383
+ : preSearchFilters?.length ? preFilteredSearch // Preserve resolvedFrom filters
2384
+ : {};
2385
+
2386
+ // Then post filters
2387
+ const postFilteredSearch = postSearchFilters?.length ? postSearchFilters.reduce((prev, next) => next(prev), destSearch) : destSearch;
2388
+ const search = replaceEqualDeep(fromSearch, postFilteredSearch);
2389
+ const searchStr = this.options.stringifySearch(search);
2390
+ const hash = dest.hash === true ? from.hash : dest.hash ? functionalUpdate(dest.hash, from.hash) : from.hash;
2391
+ const hashStr = hash ? `#${hash}` : '';
2392
+ let nextState = dest.state === true ? from.state : dest.state ? functionalUpdate(dest.state, from.state) : from.state;
2393
+ nextState = replaceEqualDeep(from.state, nextState);
1691
2394
  return {
1692
- routeSearch,
1693
- search
2395
+ pathname,
2396
+ search,
2397
+ searchStr,
2398
+ state: nextState,
2399
+ hash,
2400
+ href: `${pathname}${searchStr}${hashStr}`,
2401
+ unmaskOnReload: dest.unmaskOnReload
1694
2402
  };
1695
- } catch (err) {
1696
- if (isRedirect(err)) {
1697
- throw err;
2403
+ };
2404
+ const buildWithMatches = (dest = {}, maskedDest) => {
2405
+ let next = build(dest);
2406
+ let maskedNext = maskedDest ? build(maskedDest) : undefined;
2407
+ if (!maskedNext) {
2408
+ let params = {};
2409
+ let foundMask = this.options.routeMasks?.find(d => {
2410
+ const match = matchPathname(this.basepath, next.pathname, {
2411
+ to: d.from,
2412
+ caseSensitive: false,
2413
+ fuzzy: false
2414
+ });
2415
+ if (match) {
2416
+ params = match;
2417
+ return true;
2418
+ }
2419
+ return false;
2420
+ });
2421
+ if (foundMask) {
2422
+ foundMask = {
2423
+ ...foundMask,
2424
+ from: interpolatePath(foundMask.from, params)
2425
+ };
2426
+ maskedDest = foundMask;
2427
+ maskedNext = build(maskedDest);
2428
+ }
2429
+ }
2430
+ const nextMatches = this.matchRoutes(next.pathname, next.search);
2431
+ const maskedMatches = maskedNext ? this.matchRoutes(maskedNext.pathname, maskedNext.search) : undefined;
2432
+ const maskedFinal = maskedNext ? build(maskedDest, maskedMatches) : undefined;
2433
+ const final = build(dest, nextMatches);
2434
+ if (maskedFinal) {
2435
+ final.maskedLocation = maskedFinal;
1698
2436
  }
1699
- const errorHandler = this.route.options.onValidateSearchError ?? this.route.options.onError;
1700
- errorHandler?.(err);
1701
- const error = new Error('Invalid search params found', {
1702
- cause: err
2437
+ return final;
2438
+ };
2439
+ if (opts.mask) {
2440
+ return buildWithMatches(opts, {
2441
+ ...pick(opts, ['from']),
2442
+ ...opts.mask
1703
2443
  });
1704
- error.code = 'INVALID_SEARCH_PARAMS';
1705
- throw error;
1706
2444
  }
2445
+ return buildWithMatches(opts);
1707
2446
  };
1708
- #resolveInfo = opts => {
1709
- const {
1710
- search,
1711
- routeSearch
1712
- } = this.#resolveSearchInfo(opts);
1713
- try {
1714
- const routeContext = this.route.options.getContext?.({
1715
- parentContext: this.parentMatch?.routeContext ?? {},
1716
- context: this.parentMatch?.context ?? this.router?.options.context ?? {},
1717
- params: this.params,
1718
- search
1719
- }) || {};
1720
- const context = {
1721
- ...(this.parentMatch?.context ?? this.router?.options.context),
1722
- ...routeContext
1723
- };
1724
- return {
1725
- routeSearch,
1726
- search,
1727
- context,
1728
- routeContext
2447
+ commitLocation = async ({
2448
+ startTransition,
2449
+ ...next
2450
+ }) => {
2451
+ if (this.navigateTimeout) clearTimeout(this.navigateTimeout);
2452
+ const isSameUrl = this.latestLocation.href === next.href;
2453
+
2454
+ // If the next urls are the same and we're not replacing,
2455
+ // do nothing
2456
+ if (!isSameUrl || !next.replace) {
2457
+ let {
2458
+ maskedLocation,
2459
+ ...nextHistory
2460
+ } = next;
2461
+ if (maskedLocation) {
2462
+ nextHistory = {
2463
+ ...maskedLocation,
2464
+ state: {
2465
+ ...maskedLocation.state,
2466
+ __tempKey: undefined,
2467
+ __tempLocation: {
2468
+ ...nextHistory,
2469
+ search: nextHistory.searchStr,
2470
+ state: {
2471
+ ...nextHistory.state,
2472
+ __tempKey: undefined,
2473
+ __tempLocation: undefined,
2474
+ key: undefined
2475
+ }
2476
+ }
2477
+ }
2478
+ };
2479
+ if (nextHistory.unmaskOnReload ?? this.options.unmaskOnReload ?? false) {
2480
+ nextHistory.state.__tempKey = this.tempLocationKey;
2481
+ }
2482
+ }
2483
+ const apply = () => {
2484
+ this.history[next.replace ? 'replace' : 'push'](nextHistory.href, nextHistory.state);
1729
2485
  };
1730
- } catch (err) {
1731
- this.route.options.onError?.(err);
1732
- throw err;
2486
+ if (startTransition ?? true) {
2487
+ this.startReactTransition(apply);
2488
+ } else {
2489
+ apply();
2490
+ }
1733
2491
  }
2492
+ this.resetNextScroll = next.resetScroll ?? true;
2493
+ return this.latestLoadPromise;
2494
+ };
2495
+ buildAndCommitLocation = ({
2496
+ replace,
2497
+ resetScroll,
2498
+ startTransition,
2499
+ ...rest
2500
+ } = {}) => {
2501
+ const location = this.buildLocation(rest);
2502
+ return this.commitLocation({
2503
+ ...location,
2504
+ startTransition,
2505
+ replace,
2506
+ resetScroll
2507
+ });
1734
2508
  };
1735
- __load = async opts => {
1736
- this.parentMatch = opts.parentMatch;
1737
- let info;
2509
+ navigate = ({
2510
+ from,
2511
+ to = '',
2512
+ ...rest
2513
+ }) => {
2514
+ // If this link simply reloads the current route,
2515
+ // make sure it has a new key so it will trigger a data refresh
2516
+
2517
+ // If this `to` is a valid external URL, return
2518
+ // null for LinkUtils
2519
+ const toString = String(to);
2520
+ const fromString = typeof from === 'undefined' ? from : String(from);
2521
+ let isExternal;
1738
2522
  try {
1739
- info = this.#resolveInfo(opts);
1740
- } catch (err) {
1741
- if (isRedirect(err)) {
1742
- if (!opts?.preload) {
1743
- this.router.navigate(err);
1744
- }
1745
- return;
1746
- }
2523
+ new URL(`${toString}`);
2524
+ isExternal = true;
2525
+ } catch (e) {}
2526
+ invariant(!isExternal, 'Attempting to navigate to external url with this.navigate!');
2527
+ return this.buildAndCommitLocation({
2528
+ ...rest,
2529
+ from: fromString,
2530
+ to: toString
2531
+ });
2532
+ };
2533
+ loadMatches = async ({
2534
+ checkLatest,
2535
+ matches,
2536
+ preload
2537
+ }) => {
2538
+ let latestPromise;
2539
+ let firstBadMatchIndex;
2540
+ const updateMatch = match => {
2541
+ // const isPreload = this.state.cachedMatches.find((d) => d.id === match.id)
2542
+ const isPending = this.state.pendingMatches?.find(d => d.id === match.id);
2543
+ const isMatched = this.state.matches.find(d => d.id === match.id);
2544
+ const matchesKey = isPending ? 'pendingMatches' : isMatched ? 'matches' : 'cachedMatches';
1747
2545
  this.__store.setState(s => ({
1748
2546
  ...s,
1749
- status: 'error',
1750
- error: err
2547
+ [matchesKey]: s[matchesKey]?.map(d => d.id === match.id ? match : d)
1751
2548
  }));
2549
+ };
1752
2550
 
1753
- // Do not proceed with loading the route
1754
- return;
2551
+ // Check each match middleware to see if the route can be accessed
2552
+ try {
2553
+ for (let [index, match] of matches.entries()) {
2554
+ const parentMatch = matches[index - 1];
2555
+ const route = this.looseRoutesById[match.routeId];
2556
+ const abortController = new AbortController();
2557
+ const handleErrorAndRedirect = (err, code) => {
2558
+ err.routerCode = code;
2559
+ firstBadMatchIndex = firstBadMatchIndex ?? index;
2560
+ if (isRedirect(err)) {
2561
+ throw err;
2562
+ }
2563
+ try {
2564
+ route.options.onError?.(err);
2565
+ } catch (errorHandlerErr) {
2566
+ err = errorHandlerErr;
2567
+ if (isRedirect(errorHandlerErr)) {
2568
+ throw errorHandlerErr;
2569
+ }
2570
+ }
2571
+ matches[index] = match = {
2572
+ ...match,
2573
+ error: err,
2574
+ status: 'error',
2575
+ updatedAt: Date.now(),
2576
+ abortController: new AbortController()
2577
+ };
2578
+ };
2579
+ try {
2580
+ if (match.paramsError) {
2581
+ handleErrorAndRedirect(match.paramsError, 'PARSE_PARAMS');
2582
+ }
2583
+ if (match.searchError) {
2584
+ handleErrorAndRedirect(match.searchError, 'VALIDATE_SEARCH');
2585
+ }
2586
+ const parentContext = parentMatch?.context ?? this.options.context ?? {};
2587
+ const beforeLoadContext = (await route.options.beforeLoad?.({
2588
+ search: match.search,
2589
+ abortController,
2590
+ params: match.params,
2591
+ preload: !!preload,
2592
+ context: parentContext,
2593
+ location: this.state.location,
2594
+ // TOOD: just expose state and router, etc
2595
+ navigate: opts => this.navigate({
2596
+ ...opts,
2597
+ from: match.pathname
2598
+ }),
2599
+ buildLocation: this.buildLocation,
2600
+ cause: preload ? 'preload' : match.cause
2601
+ })) ?? {};
2602
+ if (isRedirect(beforeLoadContext)) {
2603
+ throw beforeLoadContext;
2604
+ }
2605
+ const context = {
2606
+ ...parentContext,
2607
+ ...beforeLoadContext
2608
+ };
2609
+ matches[index] = match = {
2610
+ ...match,
2611
+ routeContext: replaceEqualDeep(match.routeContext, beforeLoadContext),
2612
+ context: replaceEqualDeep(match.context, context),
2613
+ abortController
2614
+ };
2615
+ } catch (err) {
2616
+ handleErrorAndRedirect(err, 'BEFORE_LOAD');
2617
+ break;
2618
+ }
2619
+ }
2620
+ } catch (err) {
2621
+ if (isRedirect(err)) {
2622
+ if (!preload) this.navigate(err);
2623
+ return matches;
2624
+ }
2625
+ throw err;
1755
2626
  }
1756
- const {
1757
- routeSearch,
1758
- search,
1759
- context,
1760
- routeContext
1761
- } = info;
2627
+ const validResolvedMatches = matches.slice(0, firstBadMatchIndex);
2628
+ const matchPromises = [];
2629
+ validResolvedMatches.forEach((match, index) => {
2630
+ matchPromises.push(new Promise(async resolve => {
2631
+ const parentMatchPromise = matchPromises[index - 1];
2632
+ const route = this.looseRoutesById[match.routeId];
2633
+ const handleErrorAndRedirect = err => {
2634
+ if (isRedirect(err)) {
2635
+ if (!preload) {
2636
+ this.navigate(err);
2637
+ }
2638
+ return true;
2639
+ }
2640
+ return false;
2641
+ };
2642
+ let loadPromise;
2643
+ matches[index] = match = {
2644
+ ...match,
2645
+ showPending: false
2646
+ };
2647
+ let didShowPending = false;
2648
+ const pendingMs = route.options.pendingMs ?? this.options.defaultPendingMs;
2649
+ const pendingMinMs = route.options.pendingMinMs ?? this.options.defaultPendingMinMs;
2650
+ const shouldPending = !preload && pendingMs && (route.options.pendingComponent ?? this.options.defaultPendingComponent);
2651
+ const loaderContext = {
2652
+ params: match.params,
2653
+ deps: match.loaderDeps,
2654
+ preload: !!preload,
2655
+ parentMatchPromise,
2656
+ abortController: match.abortController,
2657
+ context: match.context,
2658
+ location: this.state.location,
2659
+ navigate: opts => this.navigate({
2660
+ ...opts,
2661
+ from: match.pathname
2662
+ }),
2663
+ cause: preload ? 'preload' : match.cause
2664
+ };
2665
+ const fetch = async () => {
2666
+ if (match.isFetching) {
2667
+ loadPromise = getRouteMatch(this.state, match.id)?.loadPromise;
2668
+ } else {
2669
+ // If the user doesn't want the route to reload, just
2670
+ // resolve with the existing loader data
2671
+
2672
+ if (match.fetchCount && match.status === 'success') {
2673
+ resolve();
2674
+ }
1762
2675
 
1763
- // If the match is invalid, errored or idle, trigger it to load
1764
- if (this.state.status === 'pending') {
1765
- return;
1766
- }
2676
+ // Otherwise, load the route
2677
+ matches[index] = match = {
2678
+ ...match,
2679
+ isFetching: true,
2680
+ fetchCount: match.fetchCount + 1
2681
+ };
2682
+ const componentsPromise = Promise.all(componentTypes.map(async type => {
2683
+ const component = route.options[type];
2684
+ if (component?.preload) {
2685
+ await component.preload();
2686
+ }
2687
+ }));
2688
+ const loaderPromise = route.options.loader?.(loaderContext);
2689
+ loadPromise = Promise.all([componentsPromise, loaderPromise]).then(d => d[1]);
2690
+ }
2691
+ matches[index] = match = {
2692
+ ...match,
2693
+ loadPromise
2694
+ };
2695
+ updateMatch(match);
2696
+ try {
2697
+ const loaderData = await loadPromise;
2698
+ if (latestPromise = checkLatest()) return await latestPromise;
2699
+ if (isRedirect(loaderData)) {
2700
+ if (handleErrorAndRedirect(loaderData)) return;
2701
+ }
2702
+ if (didShowPending && pendingMinMs) {
2703
+ await new Promise(r => setTimeout(r, pendingMinMs));
2704
+ }
2705
+ if (latestPromise = checkLatest()) return await latestPromise;
2706
+ matches[index] = match = {
2707
+ ...match,
2708
+ error: undefined,
2709
+ status: 'success',
2710
+ isFetching: false,
2711
+ updatedAt: Date.now(),
2712
+ loaderData,
2713
+ loadPromise: undefined
2714
+ };
2715
+ } catch (error) {
2716
+ if (latestPromise = checkLatest()) return await latestPromise;
2717
+ if (handleErrorAndRedirect(error)) return;
2718
+ try {
2719
+ route.options.onError?.(error);
2720
+ } catch (onErrorError) {
2721
+ error = onErrorError;
2722
+ if (handleErrorAndRedirect(onErrorError)) return;
2723
+ }
2724
+ matches[index] = match = {
2725
+ ...match,
2726
+ error,
2727
+ status: 'error',
2728
+ isFetching: false
2729
+ };
2730
+ }
2731
+ updateMatch(match);
2732
+ };
1767
2733
 
1768
- // TODO: Should load promises be tracked based on location?
1769
- this.__loadPromise = Promise.resolve().then(async () => {
1770
- const loadId = '' + Date.now() + Math.random();
1771
- this.#latestId = loadId;
1772
- const checkLatest = () => {
1773
- return loadId !== this.#latestId ? this.__loadPromise : undefined;
1774
- };
2734
+ // This is where all of the stale-while-revalidate magic happens
2735
+ const age = Date.now() - match.updatedAt;
2736
+ let staleAge = preload ? route.options.preloadStaleTime ?? this.options.defaultPreloadStaleTime ?? 30_000 // 30 seconds for preloads by default
2737
+ : route.options.staleTime ?? this.options.defaultStaleTime ?? 0;
2738
+
2739
+ // Default to reloading the route all the time
2740
+ let shouldReload;
2741
+ const shouldReloadOption = route.options.shouldReload;
2742
+
2743
+ // Allow shouldReload to get the last say,
2744
+ // if provided.
2745
+ shouldReload = typeof shouldReloadOption === 'function' ? shouldReloadOption(loaderContext) : shouldReloadOption;
2746
+ matches[index] = match = {
2747
+ ...match,
2748
+ preload: !!preload && !this.state.matches.find(d => d.id === match.id)
2749
+ };
2750
+ if (match.status !== 'success') {
2751
+ // If we need to potentially show the pending component,
2752
+ // start a timer to show it after the pendingMs
2753
+ if (shouldPending) {
2754
+ new Promise(r => setTimeout(r, pendingMs)).then(async () => {
2755
+ if (latestPromise = checkLatest()) return latestPromise;
2756
+ didShowPending = true;
2757
+ matches[index] = match = {
2758
+ ...match,
2759
+ showPending: true
2760
+ };
2761
+ updateMatch(match);
2762
+ resolve();
2763
+ });
2764
+ }
2765
+
2766
+ // Critical Fetching, we need to await
2767
+ await fetch();
2768
+ } else if (match.invalid || (shouldReload ?? age > staleAge)) {
2769
+ // Background Fetching, no need to wait
2770
+ fetch();
2771
+ }
2772
+ resolve();
2773
+ }));
2774
+ });
2775
+ await Promise.all(matchPromises);
2776
+ return matches;
2777
+ };
2778
+ invalidate = () => {
2779
+ const invalidate = d => ({
2780
+ ...d,
2781
+ invalid: true
2782
+ });
2783
+ this.__store.setState(s => ({
2784
+ ...s,
2785
+ matches: s.matches.map(invalidate),
2786
+ cachedMatches: s.cachedMatches.map(invalidate),
2787
+ pendingMatches: s.pendingMatches?.map(invalidate)
2788
+ }));
2789
+ this.load();
2790
+ };
2791
+ load = async () => {
2792
+ const promise = new Promise(async (resolve, reject) => {
2793
+ const next = this.latestLocation;
2794
+ const prevLocation = this.state.resolvedLocation;
2795
+ const pathDidChange = prevLocation.href !== next.href;
1775
2796
  let latestPromise;
1776
2797
 
1777
- // If the match was in an error state, set it
1778
- // to a loading state again. Otherwise, keep it
1779
- // as loading or resolved
1780
- if (this.state.status === 'idle') {
2798
+ // Cancel any pending matches
2799
+ this.cancelMatches();
2800
+ this.emit({
2801
+ type: 'onBeforeLoad',
2802
+ fromLocation: prevLocation,
2803
+ toLocation: next,
2804
+ pathChanged: pathDidChange
2805
+ });
2806
+ let pendingMatches;
2807
+ const previousMatches = this.state.matches;
2808
+ this.__store.batch(() => {
2809
+ this.cleanCache();
2810
+
2811
+ // Match the routes
2812
+ pendingMatches = this.matchRoutes(next.pathname, next.search, {
2813
+ debug: true
2814
+ });
2815
+
2816
+ // Ingest the new matches
2817
+ // If a cached moved to pendingMatches, remove it from cachedMatches
1781
2818
  this.__store.setState(s => ({
1782
2819
  ...s,
1783
- status: 'pending'
1784
- }));
1785
- }
1786
- const componentsPromise = (async () => {
1787
- // then run all component and data loaders in parallel
1788
- // For each component type, potentially load it asynchronously
1789
-
1790
- await Promise.all(componentTypes.map(async type => {
1791
- const component = this.route.options[type];
1792
- if (this[type]?.preload) {
1793
- this[type] = await this.router.options.loadComponent(component);
1794
- }
2820
+ isLoading: true,
2821
+ location: next,
2822
+ pendingMatches,
2823
+ cachedMatches: s.cachedMatches.filter(d => {
2824
+ return !pendingMatches.find(e => e.id === d.id);
2825
+ })
1795
2826
  }));
1796
- })();
1797
- const dataPromise = Promise.resolve().then(() => {
1798
- if (this.route.options.onLoad) {
1799
- return this.route.options.onLoad({
1800
- params: this.params,
1801
- routeSearch,
1802
- search,
1803
- signal: this.abortController.signal,
1804
- preload: !!opts?.preload,
1805
- routeContext: routeContext,
1806
- context: context
1807
- });
1808
- }
1809
- return;
1810
2827
  });
1811
2828
  try {
1812
- await Promise.all([componentsPromise, dataPromise]);
1813
- if (latestPromise = checkLatest()) return await latestPromise;
1814
- this.__store.setState(s => ({
1815
- ...s,
1816
- error: undefined,
1817
- status: 'success',
1818
- updatedAt: Date.now()
1819
- }));
1820
- } catch (err) {
1821
- if (isRedirect(err)) {
1822
- if (!opts?.preload) {
1823
- this.router.navigate(err);
1824
- }
1825
- return;
1826
- }
1827
- const errorHandler = this.route.options.onLoadError ?? this.route.options.onError;
1828
2829
  try {
1829
- errorHandler?.(err);
1830
- } catch (errorHandlerErr) {
1831
- if (isRedirect(errorHandlerErr)) {
1832
- if (!opts?.preload) {
1833
- this.router.navigate(errorHandlerErr);
1834
- }
1835
- return;
1836
- }
2830
+ // Load the matches
2831
+ await this.loadMatches({
2832
+ matches: pendingMatches,
2833
+ checkLatest: () => this.checkLatest(promise)
2834
+ });
2835
+ } catch (err) {
2836
+ // swallow this error, since we'll display the
2837
+ // errors on the route components
2838
+ }
2839
+
2840
+ // Only apply the latest transition
2841
+ if (latestPromise = this.checkLatest(promise)) {
2842
+ return latestPromise;
2843
+ }
2844
+ const exitingMatches = previousMatches.filter(match => !pendingMatches.find(d => d.id === match.id));
2845
+ const enteringMatches = pendingMatches.filter(match => !previousMatches.find(d => d.id === match.id));
2846
+ const stayingMatches = previousMatches.filter(match => pendingMatches.find(d => d.id === match.id));
2847
+
2848
+ // Commit the pending matches. If a previous match was
2849
+ // removed, place it in the cachedMatches
2850
+ this.__store.batch(() => {
1837
2851
  this.__store.setState(s => ({
1838
2852
  ...s,
1839
- error: errorHandlerErr,
1840
- status: 'error',
1841
- updatedAt: Date.now()
2853
+ isLoading: false,
2854
+ matches: s.pendingMatches,
2855
+ pendingMatches: undefined,
2856
+ cachedMatches: [...s.cachedMatches, ...exitingMatches.filter(d => d.status !== 'error')]
1842
2857
  }));
1843
- return;
2858
+ this.cleanCache();
2859
+ })
2860
+
2861
+ //
2862
+ ;
2863
+ [[exitingMatches, 'onLeave'], [enteringMatches, 'onEnter'], [stayingMatches, 'onStay']].forEach(([matches, hook]) => {
2864
+ matches.forEach(match => {
2865
+ this.looseRoutesById[match.routeId].options[hook]?.(match);
2866
+ });
2867
+ });
2868
+ this.emit({
2869
+ type: 'onLoad',
2870
+ fromLocation: prevLocation,
2871
+ toLocation: next,
2872
+ pathChanged: pathDidChange
2873
+ });
2874
+ resolve();
2875
+ } catch (err) {
2876
+ // Only apply the latest transition
2877
+ if (latestPromise = this.checkLatest(promise)) {
2878
+ return latestPromise;
1844
2879
  }
1845
- this.__store.setState(s => ({
1846
- ...s,
1847
- error: err,
1848
- status: 'error',
1849
- updatedAt: Date.now()
1850
- }));
1851
- } finally {
1852
- delete this.__loadPromise;
2880
+ reject(err);
1853
2881
  }
1854
2882
  });
1855
- return this.__loadPromise;
2883
+ this.latestLoadPromise = promise;
2884
+ return this.latestLoadPromise;
1856
2885
  };
1857
- #latestId = '';
1858
- }
1859
-
1860
- /**
1861
- * react-store
1862
- *
1863
- * Copyright (c) TanStack
1864
- *
1865
- * This source code is licensed under the MIT license found in the
1866
- * LICENSE.md file in the root directory of this source tree.
1867
- *
1868
- * @license MIT
1869
- */
1870
-
1871
- function useStore(store, selector = d => d, compareShallow) {
1872
- const slice = withSelector.useSyncExternalStoreWithSelector(store.subscribe, () => store.state, () => store.state, selector, compareShallow ? shallow : undefined);
1873
- return slice;
1874
- }
1875
-
1876
- //
2886
+ cleanCache = () => {
2887
+ // This is where all of the garbage collection magic happens
2888
+ this.__store.setState(s => {
2889
+ return {
2890
+ ...s,
2891
+ cachedMatches: s.cachedMatches.filter(d => {
2892
+ const route = this.looseRoutesById[d.routeId];
2893
+ if (!route.options.loader) {
2894
+ return false;
2895
+ }
1877
2896
 
1878
- function lazy(importer) {
1879
- const lazyComp = /*#__PURE__*/React__namespace.lazy(importer);
1880
- const finalComp = lazyComp;
1881
- finalComp.preload = async () => {
1882
- {
1883
- await importer();
1884
- }
2897
+ // If the route was preloaded, use the preloadGcTime
2898
+ // otherwise, use the gcTime
2899
+ const gcTime = (d.preload ? route.options.preloadGcTime ?? this.options.defaultPreloadGcTime : route.options.gcTime ?? this.options.defaultGcTime) ?? 5 * 60 * 1000;
2900
+ return d.status !== 'error' && Date.now() - d.updatedAt < gcTime;
2901
+ })
2902
+ };
2903
+ });
1885
2904
  };
1886
- return finalComp;
1887
- }
1888
- //
1889
-
1890
- function useLinkProps(options) {
1891
- const router = useRouterContext();
1892
- const {
1893
- // custom props
1894
- type,
1895
- children,
1896
- target,
1897
- activeProps = () => ({
1898
- className: 'active'
1899
- }),
1900
- inactiveProps = () => ({}),
1901
- activeOptions,
1902
- disabled,
1903
- // fromCurrent,
1904
- hash,
1905
- search,
1906
- params,
1907
- to = '.',
1908
- preload,
1909
- preloadDelay,
1910
- replace,
1911
- // element props
1912
- style,
1913
- className,
1914
- onClick,
1915
- onFocus,
1916
- onMouseEnter,
1917
- onMouseLeave,
1918
- onTouchStart,
1919
- ...rest
1920
- } = options;
1921
- const linkInfo = router.buildLink(options);
1922
- if (linkInfo.type === 'external') {
1923
- const {
1924
- href
1925
- } = linkInfo;
1926
- return {
1927
- href
2905
+ preloadRoute = async (navigateOpts = this.state.location) => {
2906
+ let next = this.buildLocation(navigateOpts);
2907
+ let matches = this.matchRoutes(next.pathname, next.search, {
2908
+ throwOnError: true
2909
+ });
2910
+ const loadedMatchIds = Object.fromEntries([...this.state.matches, ...(this.state.pendingMatches ?? []), ...this.state.cachedMatches]?.map(d => [d.id, true]));
2911
+ this.__store.batch(() => {
2912
+ matches.forEach(match => {
2913
+ if (!loadedMatchIds[match.id]) {
2914
+ this.__store.setState(s => ({
2915
+ ...s,
2916
+ cachedMatches: [...s.cachedMatches, match]
2917
+ }));
2918
+ }
2919
+ });
2920
+ });
2921
+ matches = await this.loadMatches({
2922
+ matches,
2923
+ preload: true,
2924
+ checkLatest: () => undefined
2925
+ });
2926
+ return matches;
2927
+ };
2928
+ matchRoute = (location, opts) => {
2929
+ location = {
2930
+ ...location,
2931
+ to: location.to ? this.resolvePathWithBase(location.from || '', location.to) : undefined
1928
2932
  };
1929
- }
1930
- const {
1931
- handleClick,
1932
- handleFocus,
1933
- handleEnter,
1934
- handleLeave,
1935
- handleTouchStart,
1936
- isActive,
1937
- next
1938
- } = linkInfo;
1939
- const reactHandleClick = e => {
1940
- if (React__namespace.startTransition) {
1941
- // This is a hack for react < 18
1942
- React__namespace.startTransition(() => {
1943
- handleClick(e);
2933
+ const next = this.buildLocation(location);
2934
+ if (opts?.pending && this.state.status !== 'pending') {
2935
+ return false;
2936
+ }
2937
+ const baseLocation = opts?.pending ? this.latestLocation : this.state.resolvedLocation;
2938
+ if (!baseLocation) {
2939
+ return false;
2940
+ }
2941
+ const match = matchPathname(this.basepath, baseLocation.pathname, {
2942
+ ...opts,
2943
+ to: next.pathname
2944
+ });
2945
+ if (!match) {
2946
+ return false;
2947
+ }
2948
+ if (match && (opts?.includeSearch ?? true)) {
2949
+ return deepEqual(baseLocation.search, next.search, true) ? match : false;
2950
+ }
2951
+ return match;
2952
+ };
2953
+ injectHtml = async html => {
2954
+ this.injectedHtml.push(html);
2955
+ };
2956
+ dehydrateData = (key, getData) => {
2957
+ if (typeof document === 'undefined') {
2958
+ const strKey = typeof key === 'string' ? key : JSON.stringify(key);
2959
+ this.injectHtml(async () => {
2960
+ const id = `__TSR_DEHYDRATED__${strKey}`;
2961
+ const data = typeof getData === 'function' ? await getData() : getData;
2962
+ return `<script id='${id}' suppressHydrationWarning>window["__TSR_DEHYDRATED__${escapeJSON(strKey)}"] = ${JSON.stringify(data)}
2963
+ ;(() => {
2964
+ var el = document.getElementById('${id}')
2965
+ el.parentElement.removeChild(el)
2966
+ })()
2967
+ </script>`;
1944
2968
  });
1945
- } else {
1946
- handleClick(e);
2969
+ return () => this.hydrateData(key);
1947
2970
  }
2971
+ return () => undefined;
1948
2972
  };
1949
- const composeHandlers = handlers => e => {
1950
- if (e.persist) e.persist();
1951
- handlers.filter(Boolean).forEach(handler => {
1952
- if (e.defaultPrevented) return;
1953
- handler(e);
2973
+ hydrateData = key => {
2974
+ if (typeof document !== 'undefined') {
2975
+ const strKey = typeof key === 'string' ? key : JSON.stringify(key);
2976
+ return window[`__TSR_DEHYDRATED__${strKey}`];
2977
+ }
2978
+ return undefined;
2979
+ };
2980
+ dehydrate = () => {
2981
+ return {
2982
+ state: {
2983
+ dehydratedMatches: this.state.matches.map(d => pick(d, ['id', 'status', 'updatedAt', 'loaderData']))
2984
+ }
2985
+ };
2986
+ };
2987
+ hydrate = async __do_not_use_server_ctx => {
2988
+ let _ctx = __do_not_use_server_ctx;
2989
+ // Client hydrates from window
2990
+ if (typeof document !== 'undefined') {
2991
+ _ctx = window.__TSR_DEHYDRATED__;
2992
+ }
2993
+ invariant(_ctx, 'Expected to find a __TSR_DEHYDRATED__ property on window... but we did not. Did you forget to render <DehydrateRouter /> in your app?');
2994
+ const ctx = _ctx;
2995
+ this.dehydratedData = ctx.payload;
2996
+ this.options.hydrate?.(ctx.payload);
2997
+ const dehydratedState = ctx.router.state;
2998
+ let matches = this.matchRoutes(this.state.location.pathname, this.state.location.search).map(match => {
2999
+ const dehydratedMatch = dehydratedState.dehydratedMatches.find(d => d.id === match.id);
3000
+ invariant(dehydratedMatch, `Could not find a client-side match for dehydrated match with id: ${match.id}!`);
3001
+ if (dehydratedMatch) {
3002
+ return {
3003
+ ...match,
3004
+ ...dehydratedMatch
3005
+ };
3006
+ }
3007
+ return match;
3008
+ });
3009
+ this.__store.setState(s => {
3010
+ return {
3011
+ ...s,
3012
+ matches: matches
3013
+ };
1954
3014
  });
1955
3015
  };
1956
3016
 
1957
- // Get the active props
1958
- const resolvedActiveProps = isActive ? functionalUpdate(activeProps, {}) ?? {} : {};
3017
+ // resolveMatchPromise = (matchId: string, key: string, value: any) => {
3018
+ // state.matches
3019
+ // .find((d) => d.id === matchId)
3020
+ // ?.__promisesByKey[key]?.resolve(value)
3021
+ // }
3022
+ }
1959
3023
 
1960
- // Get the inactive props
1961
- const resolvedInactiveProps = isActive ? {} : functionalUpdate(inactiveProps, {}) ?? {};
3024
+ // A function that takes an import() argument which is a function and returns a new function that will
3025
+ // proxy arguments from the caller to the imported function, retaining all type
3026
+ // information along the way
3027
+ function lazyFn(fn, key) {
3028
+ return async (...args) => {
3029
+ const imported = await fn();
3030
+ return imported[key || 'default'](...args);
3031
+ };
3032
+ }
3033
+ class SearchParamError extends Error {}
3034
+ class PathParamError extends Error {}
3035
+ function getInitialRouterState(location) {
1962
3036
  return {
1963
- ...resolvedActiveProps,
1964
- ...resolvedInactiveProps,
1965
- ...rest,
1966
- href: disabled ? undefined : next.href,
1967
- onClick: composeHandlers([onClick, reactHandleClick]),
1968
- onFocus: composeHandlers([onFocus, handleFocus]),
1969
- onMouseEnter: composeHandlers([onMouseEnter, handleEnter]),
1970
- onMouseLeave: composeHandlers([onMouseLeave, handleLeave]),
1971
- onTouchStart: composeHandlers([onTouchStart, handleTouchStart]),
1972
- target,
1973
- style: {
1974
- ...style,
1975
- ...resolvedActiveProps.style,
1976
- ...resolvedInactiveProps.style
3037
+ isLoading: false,
3038
+ isTransitioning: false,
3039
+ status: 'idle',
3040
+ resolvedLocation: {
3041
+ ...location
1977
3042
  },
1978
- className: [className, resolvedActiveProps.className, resolvedInactiveProps.className].filter(Boolean).join(' ') || undefined,
1979
- ...(disabled ? {
1980
- role: 'link',
1981
- 'aria-disabled': true
1982
- } : undefined),
1983
- ['data-status']: isActive ? 'active' : undefined
3043
+ location,
3044
+ matches: [],
3045
+ pendingMatches: [],
3046
+ cachedMatches: [],
3047
+ lastUpdated: Date.now()
1984
3048
  };
1985
3049
  }
1986
- const Link = /*#__PURE__*/React__namespace.forwardRef((props, ref) => {
1987
- const linkProps = useLinkProps(props);
1988
- return /*#__PURE__*/React__namespace.createElement("a", _extends({
1989
- ref: ref
1990
- }, linkProps, {
1991
- children: typeof props.children === 'function' ? props.children({
1992
- isActive: linkProps['data-status'] === 'active'
1993
- }) : props.children
1994
- }));
1995
- });
1996
- function Navigate(props) {
1997
- const router = useRouterContext();
1998
- React__namespace.useLayoutEffect(() => {
1999
- router.navigate(props);
2000
- }, []);
2001
- return null;
2002
- }
2003
- const matchesContext = /*#__PURE__*/React__namespace.createContext(null);
2004
- const routerContext = /*#__PURE__*/React__namespace.createContext(null);
2005
- class ReactRouter extends Router {
2006
- constructor(opts) {
2007
- super({
2008
- ...opts,
2009
- loadComponent: async component => {
2010
- if (component.preload) {
2011
- await component.preload();
3050
+
3051
+ const useLayoutEffect = typeof window !== 'undefined' ? React__namespace.useLayoutEffect : React__namespace.useEffect;
3052
+ const windowKey = 'window';
3053
+ const delimiter = '___';
3054
+ let weakScrolledElements = new WeakSet();
3055
+ const sessionsStorage = typeof window !== 'undefined' && window.sessionStorage;
3056
+ let cache = sessionsStorage ? (() => {
3057
+ const storageKey = 'tsr-scroll-restoration-v2';
3058
+ const state = JSON.parse(window.sessionStorage.getItem(storageKey) || 'null') || {
3059
+ cached: {},
3060
+ next: {}
3061
+ };
3062
+ return {
3063
+ state,
3064
+ set: updater => {
3065
+ cache.state = functionalUpdate(updater, cache.state);
3066
+ window.sessionStorage.setItem(storageKey, JSON.stringify(cache.state));
3067
+ }
3068
+ };
3069
+ })() : undefined;
3070
+ const defaultGetKey = location => location.state.key;
3071
+ function useScrollRestoration(options) {
3072
+ const router = useRouter();
3073
+ useLayoutEffect(() => {
3074
+ const getKey = options?.getKey || defaultGetKey;
3075
+ const {
3076
+ history
3077
+ } = window;
3078
+ if (history.scrollRestoration) {
3079
+ history.scrollRestoration = 'manual';
3080
+ }
3081
+ const onScroll = event => {
3082
+ if (weakScrolledElements.has(event.target)) return;
3083
+ weakScrolledElements.add(event.target);
3084
+ let elementSelector = '';
3085
+ if (event.target === document || event.target === window) {
3086
+ elementSelector = windowKey;
3087
+ } else {
3088
+ const attrId = event.target.getAttribute('data-scroll-restoration-id');
3089
+ if (attrId) {
3090
+ elementSelector = `[data-scroll-restoration-id="${attrId}"]`;
3091
+ } else {
3092
+ elementSelector = getCssSelector(event.target);
2012
3093
  }
2013
- return component;
2014
3094
  }
2015
- });
2016
- }
2017
- }
2018
- function RouterProvider({
2019
- router,
2020
- ...rest
2021
- }) {
2022
- router.update(rest);
2023
- const currentMatches = useStore(router.__store, s => s.currentMatches);
2024
- React__namespace.useEffect(router.mount, [router]);
2025
- return /*#__PURE__*/React__namespace.createElement(routerContext.Provider, {
2026
- value: {
2027
- router: router
2028
- }
2029
- }, /*#__PURE__*/React__namespace.createElement(matchesContext.Provider, {
2030
- value: [undefined, ...currentMatches]
2031
- }, /*#__PURE__*/React__namespace.createElement(CatchBoundary, {
2032
- errorComponent: ErrorComponent,
2033
- onCatch: () => {
2034
- warning(false, `Error in router! Consider setting an 'errorComponent' in your RootRoute! 👍`);
3095
+ if (!cache.state.next[elementSelector]) {
3096
+ cache.set(c => ({
3097
+ ...c,
3098
+ next: {
3099
+ ...c.next,
3100
+ [elementSelector]: {
3101
+ scrollX: NaN,
3102
+ scrollY: NaN
3103
+ }
3104
+ }
3105
+ }));
3106
+ }
3107
+ };
3108
+ if (typeof document !== 'undefined') {
3109
+ document.addEventListener('scroll', onScroll, true);
2035
3110
  }
2036
- }, /*#__PURE__*/React__namespace.createElement(Outlet, null))));
2037
- }
2038
- function useRouterContext() {
2039
- const value = React__namespace.useContext(routerContext);
2040
- warning(value, 'useRouter must be used inside a <Router> component!');
2041
- useStore(value.router.__store);
2042
- return value.router;
2043
- }
2044
- function useRouter(track, shallow) {
2045
- const router = useRouterContext();
2046
- useStore(router.__store, track, shallow);
2047
- return router;
2048
- }
2049
- function useMatches() {
2050
- return React__namespace.useContext(matchesContext);
2051
- }
2052
- function useMatch(opts) {
2053
- const router = useRouterContext();
2054
- const nearestMatch = useMatches()[0];
2055
- const match = opts?.from ? router.state.currentMatches.find(d => d.route.id === opts?.from) : nearestMatch;
2056
- invariant(match, `Could not find ${opts?.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`);
2057
- if (opts?.strict ?? true) {
2058
- invariant(nearestMatch.route.id == match?.route.id, `useMatch("${match?.route.id}") is being called in a component that is meant to render the '${nearestMatch.route.id}' route. Did you mean to 'useMatch("${match?.route.id}", { strict: false })' or 'useRoute("${match?.route.id}")' instead?`);
2059
- }
2060
- useStore(match.__store, d => opts?.track?.(match) ?? match, opts?.shallow);
2061
- return match;
2062
- }
2063
- function useRoute(routeId) {
2064
- const router = useRouterContext();
2065
- const resolvedRoute = router.getRoute(routeId);
2066
- invariant(resolvedRoute, `Could not find a route for route "${routeId}"! Did you forget to add it to your route?`);
2067
- return resolvedRoute;
2068
- }
2069
- function useSearch(opts) {
2070
- const {
2071
- track,
2072
- ...matchOpts
2073
- } = opts;
2074
- const match = useMatch(matchOpts);
2075
- useStore(match.__store, d => opts?.track?.(d.search) ?? d.search, true);
2076
- return match.state.search;
2077
- }
2078
- function useParams(opts) {
2079
- const router = useRouterContext();
2080
- useStore(router.__store, d => {
2081
- const params = last(d.currentMatches)?.params;
2082
- return opts?.track?.(params) ?? params;
2083
- }, true);
2084
- return last(router.state.currentMatches)?.params;
2085
- }
2086
- function useNavigate(defaultOpts) {
2087
- const router = useRouterContext();
2088
- return React__namespace.useCallback(opts => {
2089
- return router.navigate({
2090
- ...defaultOpts,
2091
- ...opts
3111
+ const unsubOnBeforeLoad = router.subscribe('onBeforeLoad', event => {
3112
+ if (event.pathChanged) {
3113
+ const restoreKey = getKey(event.fromLocation);
3114
+ for (const elementSelector in cache.state.next) {
3115
+ const entry = cache.state.next[elementSelector];
3116
+ if (elementSelector === windowKey) {
3117
+ entry.scrollX = window.scrollX || 0;
3118
+ entry.scrollY = window.scrollY || 0;
3119
+ } else if (elementSelector) {
3120
+ const element = document.querySelector(elementSelector);
3121
+ entry.scrollX = element?.scrollLeft || 0;
3122
+ entry.scrollY = element?.scrollTop || 0;
3123
+ }
3124
+ cache.set(c => {
3125
+ const next = {
3126
+ ...c.next
3127
+ };
3128
+ delete next[elementSelector];
3129
+ return {
3130
+ ...c,
3131
+ next,
3132
+ cached: {
3133
+ ...c.cached,
3134
+ [[restoreKey, elementSelector].join(delimiter)]: entry
3135
+ }
3136
+ };
3137
+ });
3138
+ }
3139
+ }
2092
3140
  });
2093
- }, []);
2094
- }
2095
- function useMatchRoute() {
2096
- const router = useRouterContext();
2097
- return React__namespace.useCallback(opts => {
2098
- const {
2099
- pending,
2100
- caseSensitive,
2101
- ...rest
2102
- } = opts;
2103
- return router.matchRoute(rest, {
2104
- pending,
2105
- caseSensitive
3141
+ const unsubOnResolved = router.subscribe('onResolved', event => {
3142
+ if (event.pathChanged) {
3143
+ if (!router.resetNextScroll) {
3144
+ return;
3145
+ }
3146
+ router.resetNextScroll = true;
3147
+ const getKey = options?.getKey || defaultGetKey;
3148
+ const restoreKey = getKey(event.toLocation);
3149
+ let windowRestored = false;
3150
+ for (const cacheKey in cache.state.cached) {
3151
+ const entry = cache.state.cached[cacheKey];
3152
+ const [key, elementSelector] = cacheKey.split(delimiter);
3153
+ if (key === restoreKey) {
3154
+ if (elementSelector === windowKey) {
3155
+ windowRestored = true;
3156
+ window.scrollTo(entry.scrollX, entry.scrollY);
3157
+ } else if (elementSelector) {
3158
+ const element = document.querySelector(elementSelector);
3159
+ if (element) {
3160
+ element.scrollLeft = entry.scrollX;
3161
+ element.scrollTop = entry.scrollY;
3162
+ }
3163
+ }
3164
+ }
3165
+ }
3166
+ if (!windowRestored) {
3167
+ window.scrollTo(0, 0);
3168
+ }
3169
+ cache.set(c => ({
3170
+ ...c,
3171
+ next: {}
3172
+ }));
3173
+ weakScrolledElements = new WeakSet();
3174
+ }
2106
3175
  });
3176
+ return () => {
3177
+ document.removeEventListener('scroll', onScroll);
3178
+ unsubOnBeforeLoad();
3179
+ unsubOnResolved();
3180
+ };
2107
3181
  }, []);
2108
3182
  }
2109
- function MatchRoute(props) {
2110
- const matchRoute = useMatchRoute();
2111
- const params = matchRoute(props);
2112
- if (!params) {
2113
- return null;
2114
- }
2115
- if (typeof props.children === 'function') {
2116
- return props.children(params);
2117
- }
2118
- return params ? props.children : null;
2119
- }
2120
- function Outlet() {
2121
- const matches = useMatches().slice(1);
2122
- const match = matches[0];
2123
- if (!match) {
2124
- return null;
2125
- }
2126
- return /*#__PURE__*/React__namespace.createElement(SubOutlet, {
2127
- matches: matches,
2128
- match: match
2129
- });
3183
+ function ScrollRestoration(props) {
3184
+ useScrollRestoration(props);
3185
+ return null;
2130
3186
  }
2131
- function SubOutlet({
2132
- matches,
2133
- match
2134
- }) {
2135
- const router = useRouterContext();
2136
- useStore(match.__store, store => [store.status, store.error], true);
2137
- const defaultPending = React__namespace.useCallback(() => null, []);
2138
- const PendingComponent = match.pendingComponent ?? router.options.defaultPendingComponent ?? defaultPending;
2139
- const errorComponent = match.errorComponent ?? router.options.defaultErrorComponent;
2140
- const ResolvedSuspenseBoundary = match.route.options.wrapInSuspense ?? true ? React__namespace.Suspense : SafeFragment;
2141
- const ResolvedCatchBoundary = errorComponent ? CatchBoundary : SafeFragment;
2142
- return /*#__PURE__*/React__namespace.createElement(matchesContext.Provider, {
2143
- value: matches
2144
- }, /*#__PURE__*/React__namespace.createElement(ResolvedSuspenseBoundary, {
2145
- fallback: /*#__PURE__*/React__namespace.createElement(PendingComponent, null)
2146
- }, /*#__PURE__*/React__namespace.createElement(ResolvedCatchBoundary, {
2147
- key: match.route.id,
2148
- errorComponent: errorComponent,
2149
- onCatch: () => {
2150
- warning(false, `Error in route match: ${match.id}`);
3187
+ function useElementScrollRestoration(options) {
3188
+ const router = useRouter();
3189
+ const getKey = options?.getKey || defaultGetKey;
3190
+ let elementSelector = '';
3191
+ if (options.id) {
3192
+ elementSelector = `[data-scroll-restoration-id="${options.id}"]`;
3193
+ } else {
3194
+ const element = options.getElement?.();
3195
+ if (!element) {
3196
+ return;
2151
3197
  }
2152
- }, /*#__PURE__*/React__namespace.createElement(Inner, {
2153
- match: match
2154
- }))));
2155
- }
2156
- function Inner(props) {
2157
- const router = useRouterContext();
2158
- if (props.match.state.status === 'error') {
2159
- throw props.match.state.error;
2160
- }
2161
- if (props.match.state.status === 'success') {
2162
- return /*#__PURE__*/React__namespace.createElement(props.match.component ?? router.options.defaultComponent ?? Outlet);
3198
+ elementSelector = getCssSelector(element);
2163
3199
  }
2164
- if (props.match.state.status === 'pending') {
2165
- throw props.match.__loadPromise;
3200
+ const restoreKey = getKey(router.latestLocation);
3201
+ const cacheKey = [restoreKey, elementSelector].join(delimiter);
3202
+ return cache.state.cached[cacheKey];
3203
+ }
3204
+ function getCssSelector(el) {
3205
+ let path = [],
3206
+ parent;
3207
+ while (parent = el.parentNode) {
3208
+ path.unshift(`${el.tagName}:nth-child(${[].indexOf.call(parent.children, el) + 1})`);
3209
+ el = parent;
2166
3210
  }
2167
- invariant(false, 'Idle routeMatch status encountered during rendering! You should never see this. File an issue!');
2168
- }
2169
- function SafeFragment(props) {
2170
- return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, props.children);
3211
+ return `${path.join(' > ')}`.toLowerCase();
2171
3212
  }
2172
3213
 
2173
- // This is the messiest thing ever... I'm either seriously tired (likely) or
2174
- // there has to be a better way to reset error boundaries when the
2175
- // router's location key changes.
2176
-
2177
- class CatchBoundary extends React__namespace.Component {
2178
- state = {
2179
- error: false,
2180
- info: undefined
2181
- };
2182
- componentDidCatch(error, info) {
2183
- this.props.onCatch(error, info);
2184
- console.error(error);
2185
- this.setState({
2186
- error,
2187
- info
2188
- });
2189
- }
2190
- render() {
2191
- return /*#__PURE__*/React__namespace.createElement(CatchBoundaryInner, _extends({}, this.props, {
2192
- errorState: this.state,
2193
- reset: () => this.setState({})
2194
- }));
2195
- }
2196
- }
2197
- function CatchBoundaryInner(props) {
2198
- const [activeErrorState, setActiveErrorState] = React__namespace.useState(props.errorState);
2199
- const router = useRouterContext();
2200
- const errorComponent = props.errorComponent ?? ErrorComponent;
2201
- const prevKeyRef = React__namespace.useRef('');
2202
- React__namespace.useEffect(() => {
2203
- if (activeErrorState) {
2204
- if (router.state.currentLocation.key !== prevKeyRef.current) {
2205
- setActiveErrorState({});
2206
- }
2207
- }
2208
- prevKeyRef.current = router.state.currentLocation.key;
2209
- }, [activeErrorState, router.state.currentLocation.key]);
2210
- React__namespace.useEffect(() => {
2211
- if (props.errorState.error) {
2212
- setActiveErrorState(props.errorState);
2213
- }
2214
- // props.reset()
2215
- }, [props.errorState.error]);
2216
- if (props.errorState.error && activeErrorState.error) {
2217
- return /*#__PURE__*/React__namespace.createElement(errorComponent, activeErrorState);
2218
- }
2219
- return props.children;
2220
- }
2221
- function ErrorComponent({
2222
- error
2223
- }) {
2224
- return /*#__PURE__*/React__namespace.createElement("div", {
2225
- style: {
2226
- padding: '.5rem',
2227
- maxWidth: '100%'
2228
- }
2229
- }, /*#__PURE__*/React__namespace.createElement("strong", {
2230
- style: {
2231
- fontSize: '1.2rem'
2232
- }
2233
- }, "Something went wrong!"), /*#__PURE__*/React__namespace.createElement("div", {
2234
- style: {
2235
- height: '.5rem'
2236
- }
2237
- }), /*#__PURE__*/React__namespace.createElement("div", null, /*#__PURE__*/React__namespace.createElement("pre", {
2238
- style: {
2239
- fontSize: '.7em',
2240
- border: '1px solid red',
2241
- borderRadius: '.25rem',
2242
- padding: '.5rem',
2243
- color: 'red',
2244
- overflow: 'auto'
2245
- }
2246
- }, error.message ? /*#__PURE__*/React__namespace.createElement("code", null, error.message) : null)));
2247
- }
2248
- function useBlocker(message, condition = true) {
2249
- const router = useRouter();
3214
+ function useBlocker(blockerFn, condition = true) {
3215
+ const {
3216
+ history
3217
+ } = useRouter();
2250
3218
  React__namespace.useEffect(() => {
2251
3219
  if (!condition) return;
2252
- let unblock = router.history.block((retry, cancel) => {
2253
- if (window.confirm(message)) {
2254
- unblock();
2255
- retry();
2256
- } else {
2257
- cancel();
2258
- }
2259
- });
2260
- return unblock;
3220
+ return history.block(blockerFn);
2261
3221
  });
2262
3222
  }
2263
3223
  function Block({
2264
- message,
3224
+ blocker,
2265
3225
  condition,
2266
3226
  children
2267
3227
  }) {
2268
- useBlocker(message, condition);
3228
+ useBlocker(blocker, condition);
2269
3229
  return children ?? null;
2270
3230
  }
2271
3231
 
3232
+ function useNavigate(defaultOpts) {
3233
+ const {
3234
+ navigate
3235
+ } = useRouter();
3236
+ const matchPathname = useMatch({
3237
+ strict: false,
3238
+ select: s => s.pathname
3239
+ });
3240
+ return React__namespace.useCallback(opts => {
3241
+ return navigate({
3242
+ from: opts?.to ? matchPathname : undefined,
3243
+ ...defaultOpts,
3244
+ ...opts
3245
+ });
3246
+ }, []);
3247
+ }
3248
+
3249
+ // NOTE: I don't know of anyone using this. It's undocumented, so let's wait until someone needs it
3250
+ // export function typedNavigate<
3251
+ // TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
3252
+ // TDefaultFrom extends RoutePaths<TRouteTree> = '/',
3253
+ // >(navigate: (opts: NavigateOptions<any>) => Promise<void>) {
3254
+ // return navigate as <
3255
+ // TFrom extends RoutePaths<TRouteTree> = TDefaultFrom,
3256
+ // TTo extends string = '',
3257
+ // TMaskFrom extends RoutePaths<TRouteTree> = '/',
3258
+ // TMaskTo extends string = '',
3259
+ // >(
3260
+ // opts?: NavigateOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>,
3261
+ // ) => Promise<void>
3262
+ // } //
3263
+
3264
+ function Navigate(props) {
3265
+ const {
3266
+ navigate
3267
+ } = useRouter();
3268
+ const match = useMatch({
3269
+ strict: false
3270
+ });
3271
+ React__namespace.useEffect(() => {
3272
+ navigate({
3273
+ from: props.to ? match.pathname : undefined,
3274
+ ...props
3275
+ });
3276
+ }, []);
3277
+ return null;
3278
+ }
3279
+
3280
+ exports.Await = Await;
2272
3281
  exports.Block = Block;
3282
+ exports.CatchBoundary = CatchBoundary;
3283
+ exports.CatchBoundaryImpl = CatchBoundaryImpl;
2273
3284
  exports.ErrorComponent = ErrorComponent;
3285
+ exports.FileRoute = FileRoute;
2274
3286
  exports.Link = Link;
3287
+ exports.Match = Match;
2275
3288
  exports.MatchRoute = MatchRoute;
3289
+ exports.Matches = Matches;
2276
3290
  exports.Navigate = Navigate;
3291
+ exports.NotFoundRoute = NotFoundRoute;
2277
3292
  exports.Outlet = Outlet;
2278
- exports.ReactRouter = ReactRouter;
3293
+ exports.PathParamError = PathParamError;
2279
3294
  exports.RootRoute = RootRoute;
2280
3295
  exports.Route = Route;
2281
- exports.RouteMatch = RouteMatch;
3296
+ exports.RouteApi = RouteApi;
2282
3297
  exports.Router = Router;
2283
3298
  exports.RouterProvider = RouterProvider;
3299
+ exports.ScrollRestoration = ScrollRestoration;
3300
+ exports.SearchParamError = SearchParamError;
2284
3301
  exports.cleanPath = cleanPath;
3302
+ exports.componentTypes = componentTypes;
2285
3303
  exports.createBrowserHistory = createBrowserHistory;
2286
3304
  exports.createHashHistory = createHashHistory;
3305
+ exports.createHistory = createHistory;
2287
3306
  exports.createMemoryHistory = createMemoryHistory;
3307
+ exports.createRouteMask = createRouteMask;
2288
3308
  exports.decode = decode;
2289
- exports.defaultFetchServerDataFn = defaultFetchServerDataFn;
3309
+ exports.deepEqual = deepEqual;
2290
3310
  exports.defaultParseSearch = defaultParseSearch;
2291
3311
  exports.defaultStringifySearch = defaultStringifySearch;
3312
+ exports.defer = defer;
2292
3313
  exports.encode = encode;
3314
+ exports.escapeJSON = escapeJSON;
2293
3315
  exports.functionalUpdate = functionalUpdate;
3316
+ exports.getInitialRouterState = getInitialRouterState;
3317
+ exports.getRouteMatch = getRouteMatch;
2294
3318
  exports.interpolatePath = interpolatePath;
2295
3319
  exports.invariant = invariant;
3320
+ exports.isDehydratedDeferred = isDehydratedDeferred;
2296
3321
  exports.isPlainObject = isPlainObject;
2297
3322
  exports.isRedirect = isRedirect;
3323
+ exports.isServer = isServer;
2298
3324
  exports.joinPaths = joinPaths;
2299
3325
  exports.last = last;
2300
- exports.lazy = lazy;
3326
+ exports.lazyFn = lazyFn;
3327
+ exports.lazyRouteComponent = lazyRouteComponent;
2301
3328
  exports.matchByPath = matchByPath;
3329
+ exports.matchContext = matchContext;
2302
3330
  exports.matchPathname = matchPathname;
2303
- exports.matchesContext = matchesContext;
2304
3331
  exports.parsePathname = parsePathname;
2305
3332
  exports.parseSearchWith = parseSearchWith;
2306
- exports.partialDeepEqual = partialDeepEqual;
2307
3333
  exports.pick = pick;
2308
3334
  exports.redirect = redirect;
3335
+ exports.removeBasepath = removeBasepath;
2309
3336
  exports.replaceEqualDeep = replaceEqualDeep;
2310
3337
  exports.resolvePath = resolvePath;
2311
3338
  exports.rootRouteId = rootRouteId;
2312
- exports.routerContext = routerContext;
3339
+ exports.rootRouteWithContext = rootRouteWithContext;
3340
+ exports.shallow = shallow;
2313
3341
  exports.stringifySearchWith = stringifySearchWith;
2314
3342
  exports.trimPath = trimPath;
2315
3343
  exports.trimPathLeft = trimPathLeft;
2316
3344
  exports.trimPathRight = trimPathRight;
3345
+ exports.useAwaited = useAwaited;
2317
3346
  exports.useBlocker = useBlocker;
3347
+ exports.useElementScrollRestoration = useElementScrollRestoration;
3348
+ exports.useLayoutEffect = useLayoutEffect$1;
2318
3349
  exports.useLinkProps = useLinkProps;
3350
+ exports.useLoaderData = useLoaderData;
3351
+ exports.useLoaderDeps = useLoaderDeps;
2319
3352
  exports.useMatch = useMatch;
2320
3353
  exports.useMatchRoute = useMatchRoute;
2321
3354
  exports.useMatches = useMatches;
2322
3355
  exports.useNavigate = useNavigate;
2323
3356
  exports.useParams = useParams;
2324
- exports.useRoute = useRoute;
3357
+ exports.useParentMatches = useParentMatches;
3358
+ exports.useRouteContext = useRouteContext;
2325
3359
  exports.useRouter = useRouter;
2326
- exports.useRouterContext = useRouterContext;
3360
+ exports.useRouterState = useRouterState;
3361
+ exports.useScrollRestoration = useScrollRestoration;
2327
3362
  exports.useSearch = useSearch;
2328
- exports.useStore = useStore;
3363
+ exports.useStableCallback = useStableCallback;
2329
3364
  exports.warning = warning;
2330
3365
 
2331
- Object.defineProperty(exports, '__esModule', { value: true });
2332
-
2333
3366
  }));
2334
3367
  //# sourceMappingURL=index.development.js.map