@tanstack/router-core 0.0.1-beta.14 → 0.0.1-beta.146

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