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