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

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 (60) hide show
  1. package/LICENSE +21 -0
  2. package/build/cjs/history.js +226 -0
  3. package/build/cjs/history.js.map +1 -0
  4. package/build/cjs/{packages/router-core/src/index.js → index.js} +33 -15
  5. package/build/cjs/{packages/router-core/src/index.js.map → index.js.map} +1 -1
  6. package/build/cjs/{packages/router-core/src/path.js → path.js} +45 -56
  7. package/build/cjs/path.js.map +1 -0
  8. package/build/cjs/{packages/router-core/src/qss.js → qss.js} +10 -16
  9. package/build/cjs/qss.js.map +1 -0
  10. package/build/cjs/route.js +147 -0
  11. package/build/cjs/route.js.map +1 -0
  12. package/build/cjs/router.js +1102 -0
  13. package/build/cjs/router.js.map +1 -0
  14. package/build/cjs/{packages/router-core/src/searchParams.js → searchParams.js} +11 -13
  15. package/build/cjs/searchParams.js.map +1 -0
  16. package/build/cjs/{packages/router-core/src/utils.js → utils.js} +54 -64
  17. package/build/cjs/utils.js.map +1 -0
  18. package/build/esm/index.js +1439 -2099
  19. package/build/esm/index.js.map +1 -1
  20. package/build/stats-html.html +59 -49
  21. package/build/stats-react.json +186 -249
  22. package/build/types/index.d.ts +559 -422
  23. package/build/umd/index.development.js +1675 -2232
  24. package/build/umd/index.development.js.map +1 -1
  25. package/build/umd/index.production.js +12 -2
  26. package/build/umd/index.production.js.map +1 -1
  27. package/package.json +11 -7
  28. package/src/history.ts +292 -0
  29. package/src/index.ts +2 -10
  30. package/src/link.ts +116 -113
  31. package/src/path.ts +37 -17
  32. package/src/qss.ts +1 -2
  33. package/src/route.ts +927 -218
  34. package/src/routeInfo.ts +121 -197
  35. package/src/router.ts +1481 -1018
  36. package/src/searchParams.ts +1 -1
  37. package/src/utils.ts +80 -49
  38. package/build/cjs/_virtual/_rollupPluginBabelHelpers.js +0 -33
  39. package/build/cjs/_virtual/_rollupPluginBabelHelpers.js.map +0 -1
  40. package/build/cjs/node_modules/@babel/runtime/helpers/esm/extends.js +0 -33
  41. package/build/cjs/node_modules/@babel/runtime/helpers/esm/extends.js.map +0 -1
  42. package/build/cjs/node_modules/history/index.js +0 -815
  43. package/build/cjs/node_modules/history/index.js.map +0 -1
  44. package/build/cjs/node_modules/tiny-invariant/dist/esm/tiny-invariant.js +0 -30
  45. package/build/cjs/node_modules/tiny-invariant/dist/esm/tiny-invariant.js.map +0 -1
  46. package/build/cjs/packages/router-core/src/path.js.map +0 -1
  47. package/build/cjs/packages/router-core/src/qss.js.map +0 -1
  48. package/build/cjs/packages/router-core/src/route.js +0 -147
  49. package/build/cjs/packages/router-core/src/route.js.map +0 -1
  50. package/build/cjs/packages/router-core/src/routeConfig.js +0 -69
  51. package/build/cjs/packages/router-core/src/routeConfig.js.map +0 -1
  52. package/build/cjs/packages/router-core/src/routeMatch.js +0 -226
  53. package/build/cjs/packages/router-core/src/routeMatch.js.map +0 -1
  54. package/build/cjs/packages/router-core/src/router.js +0 -832
  55. package/build/cjs/packages/router-core/src/router.js.map +0 -1
  56. package/build/cjs/packages/router-core/src/searchParams.js.map +0 -1
  57. package/build/cjs/packages/router-core/src/utils.js.map +0 -1
  58. package/src/frameworks.ts +0 -11
  59. package/src/routeConfig.ts +0 -489
  60. 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,1386 @@
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
- }
147
+ };
161
148
  }
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
- }), '');
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
+ });
178
194
  }
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
- }));
195
+ function createHashHistory() {
196
+ return createBrowserHistory({
197
+ getHref: () => window.location.hash.substring(1),
198
+ createHref: path => `#${path}`
199
+ });
198
200
  }
199
-
200
- function getHistoryStateAndUrl(nextLocation, index) {
201
- return [{
202
- usr: nextLocation.state,
203
- key: nextLocation.key,
204
- idx: index
205
- }, createHref(nextLocation)];
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
234
+ });
206
235
  }
207
-
208
- function allowTx(action, location, retry) {
209
- return !blockers.length || (blockers.call({
210
- action: action,
211
- location: location,
212
- retry: retry
213
- }), false);
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
+ };
214
246
  }
215
247
 
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
226
- });
248
+ // Thanks co-pilot!
249
+ function createRandomKey() {
250
+ return (Math.random() + 1).toString(36).substring(7);
227
251
  }
228
252
 
229
- function push(to, state) {
230
- var nextAction = Action.Push;
231
- var nextLocation = getNextLocation(to, state);
232
-
233
- function retry() {
234
- push(to, state);
253
+ function last(arr) {
254
+ return arr[arr.length - 1];
255
+ }
256
+ function isFunction(d) {
257
+ return typeof d === 'function';
258
+ }
259
+ function functionalUpdate(updater, previous) {
260
+ if (isFunction(updater)) {
261
+ return updater(previous);
235
262
  }
263
+ return updater;
264
+ }
265
+ function pick(parent, keys) {
266
+ return keys.reduce((obj, key) => {
267
+ obj[key] = parent[key];
268
+ return obj;
269
+ }, {});
270
+ }
236
271
 
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);
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++;
295
+ }
250
296
  }
251
-
252
- applyTx(nextAction);
297
+ return prevSize === nextSize && equalItems === prevSize ? prev : copy;
253
298
  }
299
+ return next;
254
300
  }
255
301
 
256
- function replace(to, state) {
257
- var nextAction = Action.Replace;
258
- var nextLocation = getNextLocation(to, state);
302
+ // Copied from: https://github.com/jonschlinkert/is-plain-object
303
+ function isPlainObject(o) {
304
+ if (!hasObjectPrototype(o)) {
305
+ return false;
306
+ }
259
307
 
260
- function retry() {
261
- replace(to, state);
308
+ // If has modified constructor
309
+ const ctor = o.constructor;
310
+ if (typeof ctor === 'undefined') {
311
+ return true;
262
312
  }
263
313
 
264
- if (allowTx(nextAction, nextLocation, retry)) {
265
- var _getHistoryStateAndUr2 = getHistoryStateAndUrl(nextLocation, index),
266
- historyState = _getHistoryStateAndUr2[0],
267
- url = _getHistoryStateAndUr2[1]; // TODO: Support forced reloading
314
+ // If has modified prototype
315
+ const prot = ctor.prototype;
316
+ if (!hasObjectPrototype(prot)) {
317
+ return false;
318
+ }
268
319
 
320
+ // If constructor does not have an Object-specific method
321
+ if (!prot.hasOwnProperty('isPrototypeOf')) {
322
+ return false;
323
+ }
269
324
 
270
- globalHistory.replaceState(historyState, '', url);
271
- applyTx(nextAction);
325
+ // Most likely a plain Object
326
+ return true;
327
+ }
328
+ function hasObjectPrototype(o) {
329
+ return Object.prototype.toString.call(o) === '[object Object]';
330
+ }
331
+ function partialDeepEqual(a, b) {
332
+ if (a === b) {
333
+ return true;
334
+ }
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]));
272
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;
273
345
  }
274
346
 
275
- function go(delta) {
276
- globalHistory.go(delta);
347
+ function joinPaths(paths) {
348
+ return cleanPath(paths.filter(Boolean).join('/'));
277
349
  }
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);
350
+ function cleanPath(path) {
351
+ // remove double slashes
352
+ return path.replace(/\/{2,}/g, '/');
353
+ }
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);
306
387
  }
388
+ });
389
+ const joined = joinPaths([basepath, ...baseSegments.map(d => d.value)]);
390
+ return cleanPath(joined);
391
+ }
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;
407
+ }
307
408
 
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
- }
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
+ };
417
+ }
418
+ if (part.charAt(0) === '$') {
419
+ return {
420
+ type: 'param',
421
+ value: part
422
+ };
423
+ }
424
+ return {
425
+ type: 'pathname',
426
+ value: part
316
427
  };
428
+ }));
429
+ if (pathname.slice(-1) === '/') {
430
+ pathname = pathname.substring(1);
431
+ segments.push({
432
+ type: 'pathname',
433
+ value: '/'
434
+ });
317
435
  }
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 = {};
436
+ return segments;
333
437
  }
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
- })];
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
+ }));
357
451
  }
452
+ function matchPathname(basepath, currentPathname, matchLocation) {
453
+ const pathParams = matchByPath(basepath, currentPathname, matchLocation);
454
+ // const searchMatched = matchBySearch(location.search, matchLocation)
358
455
 
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);
456
+ if (matchLocation.to && !pathParams) {
457
+ return;
458
+ }
459
+ return pathParams ?? {};
460
+ }
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;
383
507
  }
384
- };
385
- go(delta);
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
+ }
386
520
  }
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.") ;
394
521
  }
395
- } else {
396
- applyTx(nextAction);
522
+ if (!isLastBaseSegment && isLastRouteSegment) {
523
+ return !!matchLocation.fuzzy;
524
+ }
397
525
  }
398
- }
526
+ return true;
527
+ })();
528
+ return isMatch ? params : undefined;
399
529
  }
400
530
 
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();
411
- }
412
- });
413
- var action = Action.Pop;
414
-
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
- }
531
+ // @ts-nocheck
428
532
 
429
- function getBaseHref() {
430
- var base = document.querySelector('base');
431
- var href = '';
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.
432
534
 
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);
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);
550
+ }
551
+ }
437
552
  }
438
-
439
- return href;
553
+ return (pfx || '') + str;
440
554
  }
441
-
442
- function createHref(to) {
443
- return getBaseHref() + '#' + (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;
444
561
  }
445
-
446
- function getNextLocation(to, state) {
447
- if (state === void 0) {
448
- 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
+ }
449
575
  }
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
- }));
576
+ return out;
459
577
  }
460
578
 
461
- function getHistoryStateAndUrl(nextLocation, index) {
462
- return [{
463
- usr: nextLocation.state,
464
- key: nextLocation.key,
465
- idx: index
466
- }, createHref(nextLocation)];
467
- }
579
+ const rootRouteId = '__root__';
468
580
 
469
- function allowTx(action, location, retry) {
470
- return !blockers.length || (blockers.call({
471
- action: action,
472
- location: location,
473
- retry: retry
474
- }), false);
475
- }
581
+ // | ParseParamsObj<TPath, TParams>
476
582
 
477
- function applyTx(nextAction) {
478
- action = nextAction;
583
+ // The parse type here allows a zod schema to be passed directly to the validator
479
584
 
480
- var _getIndexAndLocation7 = getIndexAndLocation();
585
+ class Route {
586
+ // Set up in this.init()
481
587
 
482
- index = _getIndexAndLocation7[0];
483
- location = _getIndexAndLocation7[1];
484
- listeners.call({
485
- action: action,
486
- location: location
487
- });
488
- }
588
+ // customId!: TCustomId
489
589
 
490
- function push(to, state) {
491
- var nextAction = Action.Push;
492
- var nextLocation = getNextLocation(to, state);
590
+ // Optional
493
591
 
494
- function retry() {
495
- push(to, state);
592
+ constructor(options) {
593
+ this.options = options || {};
594
+ this.isRoot = !options?.getParentRoute;
595
+ Route.__onInit(this);
496
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;
497
609
 
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);
610
+ // If the path is anything other than an index path, trim it up
611
+ if (path && path !== '/') {
612
+ path = trimPath(path);
513
613
  }
614
+ const customId = options?.id || path;
514
615
 
515
- applyTx(nextAction);
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
+ };
643
+ }
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);
516
653
  }
517
654
  }
518
655
 
519
- function replace(to, state) {
520
- var nextAction = Action.Replace;
521
- var nextLocation = getNextLocation(to, state);
522
-
523
- function retry() {
524
- replace(to, state);
656
+ // const rootRoute = new RootRoute({
657
+ // validateSearch: () => null as unknown as { root?: boolean },
658
+ // })
659
+
660
+ // const aRoute = new Route({
661
+ // getParentRoute: () => rootRoute,
662
+ // path: 'a',
663
+ // validateSearch: () => null as unknown as { a?: string },
664
+ // })
665
+
666
+ // const bRoute = new Route({
667
+ // getParentRoute: () => aRoute,
668
+ // path: 'b',
669
+ // })
670
+
671
+ // const rootIsRoot = rootRoute.isRoot
672
+ // // ^?
673
+ // const aIsRoot = aRoute.isRoot
674
+ // // ^?
675
+
676
+ // const rId = rootRoute.id
677
+ // // ^?
678
+ // const aId = aRoute.id
679
+ // // ^?
680
+ // const bId = bRoute.id
681
+ // // ^?
682
+
683
+ // const rPath = rootRoute.fullPath
684
+ // // ^?
685
+ // const aPath = aRoute.fullPath
686
+ // // ^?
687
+ // const bPath = bRoute.fullPath
688
+ // // ^?
689
+
690
+ // const rSearch = rootRoute.__types.fullSearchSchema
691
+ // // ^?
692
+ // const aSearch = aRoute.__types.fullSearchSchema
693
+ // // ^?
694
+ // const bSearch = bRoute.__types.fullSearchSchema
695
+ // // ^?
696
+
697
+ // const config = rootRoute.addChildren([aRoute.addChildren([bRoute])])
698
+ // // ^?
699
+
700
+ /**
701
+ * @tanstack/store/src/index.ts
702
+ *
703
+ * Copyright (c) TanStack
704
+ *
705
+ * This source code is licensed under the MIT license found in the
706
+ * LICENSE.md file in the root directory of this source tree.
707
+ *
708
+ * @license MIT
709
+ */
710
+ class Store {
711
+ listeners = new Set();
712
+ _batching = false;
713
+ _flushing = 0;
714
+ _nextPriority = null;
715
+ constructor(initialState, options) {
716
+ this.state = initialState;
717
+ this.options = options;
525
718
  }
719
+ subscribe = listener => {
720
+ this.listeners.add(listener);
721
+ const unsub = this.options?.onSubscribe?.(listener, this);
722
+ return () => {
723
+ this.listeners.delete(listener);
724
+ unsub?.();
725
+ };
726
+ };
727
+ setState = (updater, opts) => {
728
+ const previous = this.state;
729
+ this.state = this.options?.updateFn ? this.options.updateFn(previous)(updater) : updater(previous);
730
+ const priority = opts?.priority ?? this.options?.defaultPriority ?? 'high';
731
+ if (this._nextPriority === null) {
732
+ this._nextPriority = priority;
733
+ } else if (this._nextPriority === 'high') {
734
+ this._nextPriority = priority;
735
+ } else {
736
+ this._nextPriority = this.options?.defaultPriority ?? 'high';
737
+ }
526
738
 
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);
537
- }
538
- }
739
+ // Always run onUpdate, regardless of batching
740
+ this.options?.onUpdate?.({
741
+ priority: this._nextPriority
742
+ });
539
743
 
540
- function go(delta) {
541
- globalHistory.go(delta);
744
+ // Attempt to flush
745
+ this._flush();
746
+ };
747
+ _flush = () => {
748
+ if (this._batching) return;
749
+ const flushId = ++this._flushing;
750
+ this.listeners.forEach(listener => {
751
+ if (this._flushing !== flushId) return;
752
+ listener({
753
+ priority: this._nextPriority ?? 'high'
754
+ });
755
+ });
756
+ };
757
+ batch = cb => {
758
+ if (this._batching) return cb();
759
+ this._batching = true;
760
+ cb();
761
+ this._batching = false;
762
+ this._flush();
763
+ };
542
764
  }
543
765
 
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);
766
+ const defaultParseSearch = parseSearchWith(JSON.parse);
767
+ const defaultStringifySearch = stringifySearchWith(JSON.stringify);
768
+ function parseSearchWith(parser) {
769
+ return searchStr => {
770
+ if (searchStr.substring(0, 1) === '?') {
771
+ searchStr = searchStr.substring(1);
571
772
  }
773
+ let query = decode(searchStr);
572
774
 
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
577
-
578
- if (!blockers.length) {
579
- window.removeEventListener(BeforeUnloadEventType, promptBeforeUnload);
775
+ // Try to parse any query params that might be json
776
+ for (let key in query) {
777
+ const value = query[key];
778
+ if (typeof value === 'string') {
779
+ try {
780
+ query[key] = parser(value);
781
+ } catch (err) {
782
+ //
783
+ }
580
784
  }
581
- };
582
- }
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 = {};
785
+ }
786
+ return query;
787
+ };
596
788
  }
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);
789
+ function stringifySearchWith(stringify) {
790
+ return search => {
791
+ search = {
792
+ ...search
793
+ };
794
+ if (search) {
795
+ Object.keys(search).forEach(key => {
796
+ const val = search[key];
797
+ if (typeof val === 'undefined' || val === undefined) {
798
+ delete search[key];
799
+ } else if (val && typeof val === 'object' && val !== null) {
800
+ try {
801
+ search[key] = stringify(val);
802
+ } catch (err) {
803
+ // silent
804
+ }
805
+ }
806
+ });
807
+ }
808
+ const searchStr = encode(search).toString();
809
+ return searchStr ? `?${searchStr}` : '';
810
+ };
621
811
  }
622
812
 
623
- function getNextLocation(to, state) {
624
- if (state === void 0) {
625
- state = null;
626
- }
813
+ //
814
+
815
+ const componentTypes = ['component', 'errorComponent', 'pendingComponent'];
816
+ class Router {
817
+ #unsubHistory;
818
+ constructor(options) {
819
+ this.options = {
820
+ defaultPreloadDelay: 50,
821
+ context: undefined,
822
+ ...options,
823
+ stringifySearch: options?.stringifySearch ?? defaultStringifySearch,
824
+ parseSearch: options?.parseSearch ?? defaultParseSearch
825
+ // fetchServerDataFn: options?.fetchServerDataFn ?? defaultFetchServerDataFn,
826
+ };
627
827
 
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);
644
- }
645
-
646
- function applyTx(nextAction, nextLocation) {
647
- action = nextAction;
648
- location = nextLocation;
649
- listeners.call({
650
- action: action,
651
- location: location
652
- });
653
- }
654
-
655
- function push(to, state) {
656
- var nextAction = Action.Push;
657
- var nextLocation = getNextLocation(to, state);
658
-
659
- function retry() {
660
- push(to, state);
661
- }
662
-
663
- warning$1(location.pathname.charAt(0) === '/', "Relative pathnames are not supported in memory history.push(" + JSON.stringify(to) + ")") ;
664
-
665
- if (allowTx(nextAction, nextLocation, retry)) {
666
- index += 1;
667
- entries.splice(index, entries.length, nextLocation);
668
- applyTx(nextAction, nextLocation);
669
- }
670
- }
671
-
672
- function replace(to, state) {
673
- var nextAction = Action.Replace;
674
- var nextLocation = getNextLocation(to, state);
675
-
676
- function retry() {
677
- replace(to, state);
678
- }
679
-
680
- warning$1(location.pathname.charAt(0) === '/', "Relative pathnames are not supported in memory history.replace(" + JSON.stringify(to) + ")") ;
681
-
682
- if (allowTx(nextAction, nextLocation, retry)) {
683
- entries[index] = nextLocation;
684
- applyTx(nextAction, nextLocation);
685
- }
686
- }
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);
695
- }
696
-
697
- if (allowTx(nextAction, nextLocation, retry)) {
698
- index = nextIndex;
699
- applyTx(nextAction, nextLocation);
700
- }
701
- }
702
-
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);
731
- }
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
- });
762
- };
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++;
866
- }
867
- }
868
-
869
- return aSize === bSize && equalItems === aSize ? prev : copy;
870
- }
871
-
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
- }
925
-
926
- function functionalUpdate(updater, previous) {
927
- if (isFunction(updater)) {
928
- return updater(previous);
929
- }
930
-
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();
976
- }
977
-
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
- });
1002
- }
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
1022
- };
1023
- }
1024
-
1025
- return {
1026
- type: 'pathname',
1027
- value: part
1028
- };
1029
- }));
1030
-
1031
- if (pathname.slice(-1) === '/') {
1032
- pathname = pathname.substring(1);
1033
- segments.push({
1034
- type: 'pathname',
1035
- value: '/'
1036
- });
1037
- }
1038
-
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
- }
1097
-
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
- }
828
+ this.__store = new Store(getInitialRouterState(), {
829
+ onUpdate: () => {
830
+ const prev = this.state;
831
+ this.state = this.__store.state;
832
+ const matchesByIdChanged = prev.matchesById !== this.state.matchesById;
833
+ let matchesChanged;
834
+ let pendingMatchesChanged;
835
+ if (!matchesByIdChanged) {
836
+ matchesChanged = prev.matchIds.length !== this.state.matchIds.length || prev.matchIds.some((d, i) => d !== this.state.matchIds[i]);
837
+ pendingMatchesChanged = prev.pendingMatchIds.length !== this.state.pendingMatchIds.length || prev.pendingMatchIds.some((d, i) => d !== this.state.pendingMatchIds[i]);
1106
838
  }
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;
839
+ if (matchesByIdChanged || matchesChanged) {
840
+ this.state.matches = this.state.matchIds.map(id => {
841
+ return this.state.matchesById[id];
842
+ });
1116
843
  }
1117
-
1118
- if (!baseSegment.value.startsWith(':')) {
1119
- params[routeSegment.value.substring(1)] = baseSegment.value;
844
+ if (matchesByIdChanged || pendingMatchesChanged) {
845
+ this.state.pendingMatches = this.state.pendingMatchIds.map(id => {
846
+ return this.state.matchesById[id];
847
+ });
1120
848
  }
1121
- }
1122
- }
1123
-
1124
- if (isLastRouteSegment && !isLastBaseSegment) {
1125
- return !!matchLocation.fuzzy;
849
+ this.state.isFetching = [...this.state.matches, ...this.state.pendingMatches].some(d => d.isFetching);
850
+ },
851
+ defaultPriority: 'low'
852
+ });
853
+ this.state = this.__store.state;
854
+ this.update(options);
855
+ const next = this.buildNext({
856
+ hash: true,
857
+ fromCurrent: true,
858
+ search: true,
859
+ state: true
860
+ });
861
+ if (this.state.location.href !== next.href) {
862
+ this.#commitLocation({
863
+ ...next,
864
+ replace: true
865
+ });
1126
866
  }
1127
867
  }
868
+ reset = () => {
869
+ this.__store.setState(s => Object.assign(s, getInitialRouterState()));
870
+ };
871
+ mount = () => {
872
+ // If the router matches are empty, start loading the matches
873
+ // if (!this.state.matches.length) {
874
+ this.safeLoad();
875
+ // }
876
+ };
1128
877
 
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]);
878
+ update = opts => {
879
+ this.options = {
880
+ ...this.options,
881
+ ...opts,
882
+ context: {
883
+ ...this.options.context,
884
+ ...opts?.context
1149
885
  }
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];
886
+ };
887
+ if (!this.history || this.options.history && this.options.history !== this.history) {
888
+ if (this.#unsubHistory) {
889
+ this.#unsubHistory();
1197
890
  }
891
+ this.history = this.options.history ?? (isServer ? createMemoryHistory() : createBrowserHistory());
892
+ const parsedLocation = this.#parseLocation();
893
+ this.__store.setState(s => ({
894
+ ...s,
895
+ resolvedLocation: parsedLocation,
896
+ location: parsedLocation
897
+ }));
898
+ this.#unsubHistory = this.history.listen(() => {
899
+ this.safeLoad({
900
+ next: this.#parseLocation(this.state.location)
901
+ });
902
+ });
1198
903
  }
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
- }
904
+ const {
905
+ basepath,
906
+ routeTree
907
+ } = this.options;
908
+ this.basepath = `/${trimPath(basepath ?? '') ?? ''}`;
909
+ if (routeTree && routeTree !== this.routeTree) {
910
+ this.#buildRouteTree(routeTree);
1262
911
  }
912
+ return this;
1263
913
  };
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
- }
914
+ buildNext = opts => {
915
+ const next = this.#buildLocation(opts);
916
+ const __matches = this.matchRoutes(next.pathname, next.search);
917
+ return this.#buildLocation({
918
+ ...opts,
919
+ __matches
920
+ });
1297
921
  };
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
922
+ cancelMatches = () => {
923
+ this.state.matches.forEach(match => {
924
+ this.cancelMatch(match.id);
925
+ });
926
+ };
927
+ cancelMatch = id => {
928
+ this.getRouteMatch(id)?.abortController?.abort();
929
+ };
930
+ safeLoad = opts => {
931
+ return this.load(opts).catch(err => {
932
+ // console.warn(err)
933
+ // invariant(false, 'Encountered an error during router.load()! ☝️.')
934
+ });
935
+ };
936
+ latestLoadPromise = Promise.resolve();
937
+ load = async opts => {
938
+ const promise = new Promise(async (resolve, reject) => {
939
+ let latestPromise;
940
+ const checkLatest = () => {
941
+ return this.latestLoadPromise !== promise ? this.latestLoadPromise : undefined;
942
+ };
1424
943
 
1425
- if (prevSearch !== nextSearch) {
1426
- routeMatch.isInvalid = true;
944
+ // Cancel any pending matches
945
+ // this.cancelMatches()
946
+
947
+ let pendingMatches;
948
+ this.__store.batch(() => {
949
+ if (opts?.next) {
950
+ // Ingest the new location
951
+ this.__store.setState(s => ({
952
+ ...s,
953
+ location: opts.next
954
+ }));
1427
955
  }
1428
956
 
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
957
+ // Match the routes
958
+ pendingMatches = this.matchRoutes(this.state.location.pathname, this.state.location.search, {
959
+ throwOnError: opts?.throwOnError,
960
+ debug: true
1442
961
  });
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
- }
962
+ this.__store.setState(s => ({
963
+ ...s,
964
+ status: 'pending',
965
+ pendingMatchIds: pendingMatches.map(d => d.id),
966
+ matchesById: this.#mergeMatches(s.matchesById, pendingMatches)
1519
967
  }));
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
968
  });
1558
-
1559
969
  try {
1560
- await Promise.all([routeMatch.__.componentsPromise, routeMatch.__.dataPromise]);
970
+ // Load the matches
971
+ await this.loadMatches(pendingMatches);
1561
972
 
1562
- if (id !== routeMatch.__.latestId) {
1563
- return routeMatch.__.loadPromise;
973
+ // Only apply the latest transition
974
+ if (latestPromise = checkLatest()) {
975
+ return await latestPromise;
1564
976
  }
1565
- } finally {
1566
- if (id !== routeMatch.__.latestId) {
1567
- return routeMatch.__.loadPromise;
977
+ const prevLocation = this.state.resolvedLocation;
978
+ this.__store.setState(s => ({
979
+ ...s,
980
+ status: 'idle',
981
+ resolvedLocation: s.location,
982
+ matchIds: s.pendingMatchIds,
983
+ pendingMatchIds: []
984
+ }));
985
+ if (prevLocation.href !== this.state.location.href) {
986
+ this.options.onRouteChange?.();
1568
987
  }
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
988
+ resolve();
989
+ } catch (err) {
990
+ // Only apply the latest transition
991
+ if (latestPromise = checkLatest()) {
992
+ return await latestPromise;
1630
993
  }
994
+ reject(err);
1631
995
  }
1632
996
  });
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
- }
997
+ this.latestLoadPromise = promise;
998
+ return this.latestLoadPromise;
999
+ };
1000
+ #mergeMatches = (prevMatchesById, nextMatches) => {
1001
+ const nextMatchesById = {
1002
+ ...prevMatchesById
1773
1003
  };
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;
1004
+ let hadNew = false;
1005
+ nextMatches.forEach(match => {
1006
+ if (!nextMatchesById[match.id]) {
1007
+ hadNew = true;
1008
+ nextMatchesById[match.id] = match;
1784
1009
  }
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
1010
  });
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
- });
1011
+ if (!hadNew) {
1012
+ return prevMatchesById;
1840
1013
  }
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;
1849
- }
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
- }
1014
+ return nextMatchesById;
1015
+ };
1016
+ getRoute = id => {
1017
+ const route = this.routesById[id];
1018
+ invariant(route, `Route with id "${id}" not found`);
1019
+ return route;
1020
+ };
1021
+ preloadRoute = async (navigateOpts = this.state.location) => {
1022
+ const next = this.buildNext(navigateOpts);
1023
+ const matches = this.matchRoutes(next.pathname, next.search, {
1024
+ throwOnError: true
1860
1025
  });
1861
- const entering = matches.filter(d => {
1862
- return !previousMatches.find(dd => dd.matchId === d.matchId);
1026
+ this.__store.setState(s => {
1027
+ return {
1028
+ ...s,
1029
+ matchesById: this.#mergeMatches(s.matchesById, matches)
1030
+ };
1031
+ });
1032
+ await this.loadMatches(matches, {
1033
+ preload: true,
1034
+ maxAge: navigateOpts.maxAge
1863
1035
  });
1036
+ return matches;
1037
+ };
1038
+ cleanMatches = () => {
1864
1039
  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
1040
+ const outdatedMatchIds = Object.values(this.state.matchesById).filter(match => {
1041
+ const route = this.getRoute(match.routeId);
1042
+ 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);
1043
+ }).map(d => d.id);
1044
+ if (outdatedMatchIds.length) {
1045
+ this.__store.setState(s => {
1046
+ const matchesById = {
1047
+ ...s.matchesById
1048
+ };
1049
+ outdatedMatchIds.forEach(id => {
1050
+ delete matchesById[id];
1051
+ });
1052
+ return {
1053
+ ...s,
1054
+ matchesById
1884
1055
  };
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
1056
  });
1898
- delete router.matchCache[d.matchId];
1899
- });
1900
-
1901
- if (router.startedLoadingAt !== id) {
1902
- // Ignore side-effects of match loading
1903
- return;
1904
1057
  }
1905
-
1906
- matches.forEach(match => {
1907
- // Clear actions
1908
- if (match.action) {
1909
- match.action.current = undefined;
1910
- match.action.submissions = [];
1058
+ };
1059
+ matchRoutes = (pathname, locationSearch, opts) => {
1060
+ let routeParams = {};
1061
+ let foundRoute = this.flatRoutes.find(route => {
1062
+ const matchedParams = matchPathname(this.basepath, pathname, {
1063
+ to: route.fullPath,
1064
+ caseSensitive: route.options.caseSensitive ?? this.options.caseSensitive
1065
+ });
1066
+ if (matchedParams) {
1067
+ routeParams = matchedParams;
1068
+ return true;
1911
1069
  }
1070
+ return false;
1912
1071
  });
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;
1072
+ let routeCursor = foundRoute || this.routesById['__root__'];
1073
+ let matchedRoutes = [routeCursor];
1074
+ while (routeCursor?.parentRoute) {
1075
+ routeCursor = routeCursor.parentRoute;
1076
+ if (routeCursor) matchedRoutes.unshift(routeCursor);
1957
1077
  }
1958
1078
 
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
- }
1079
+ // Alright, by now we should have all of our
1080
+ // matching routes and their param pairs, let's
1081
+ // Turn them into actual `Match` objects and
1082
+ // accumulate the params into a single params bag
1083
+ let allParams = {};
1979
1084
 
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 : [])];
1085
+ // Existing matches are matches that are already loaded along with
1086
+ // pending matches that are still loading
1981
1087
 
1982
- const recurse = async routes => {
1983
- var _parentMatch$params, _router$options$filte, _foundRoute$childRout;
1088
+ const matches = matchedRoutes.map(route => {
1089
+ let parsedParams;
1090
+ let parsedParamsError;
1091
+ try {
1092
+ parsedParams = route.options.parseParams?.(routeParams) ?? routeParams;
1093
+ // (typeof route.options.parseParams === 'object' &&
1094
+ // route.options.parseParams.parse
1095
+ // ? route.options.parseParams.parse(routeParams)
1096
+ // : (route.options.parseParams as any)?.(routeParams!)) ?? routeParams
1097
+ } catch (err) {
1098
+ parsedParamsError = new PathParamError(err.message, {
1099
+ cause: err
1100
+ });
1101
+ if (opts?.throwOnError) {
1102
+ throw parsedParamsError;
1103
+ }
1104
+ }
1984
1105
 
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 = [];
1106
+ // Add the parsed params to the accumulated params bag
1107
+ Object.assign(allParams, parsedParams);
1108
+ const interpolatedPath = interpolatePath(route.path, allParams);
1109
+ const key = route.options.key ? route.options.key({
1110
+ params: allParams,
1111
+ search: locationSearch
1112
+ }) ?? '' : '';
1113
+ const stringifiedKey = key ? JSON.stringify(key) : '';
1114
+ const matchId = interpolatePath(route.id, allParams, true) + stringifiedKey;
1115
+
1116
+ // Waste not, want not. If we already have a match for this route,
1117
+ // reuse it. This is important for layout routes, which might stick
1118
+ // around between navigation actions that only change leaf routes.
1119
+ const existingMatch = this.getRouteMatch(matchId);
1120
+ if (existingMatch) {
1121
+ return {
1122
+ ...existingMatch
1123
+ };
1124
+ }
1989
1125
 
1990
- const findMatchInRoutes = (parentRoutes, routes) => {
1991
- routes.some(route => {
1992
- var _route$childRoutes, _route$childRoutes2, _route$options$caseSe;
1126
+ // Create a fresh route match
1127
+ const hasLoaders = !!(route.options.loader || componentTypes.some(d => route.options[d]?.preload));
1128
+ const routeMatch = {
1129
+ id: matchId,
1130
+ key: stringifiedKey,
1131
+ routeId: route.id,
1132
+ params: allParams,
1133
+ pathname: joinPaths([this.basepath, interpolatedPath]),
1134
+ updatedAt: Date.now(),
1135
+ invalidAt: Infinity,
1136
+ preloadInvalidAt: Infinity,
1137
+ routeSearch: {},
1138
+ search: {},
1139
+ status: hasLoaders ? 'idle' : 'success',
1140
+ isFetching: false,
1141
+ invalid: false,
1142
+ error: undefined,
1143
+ paramsError: parsedParamsError,
1144
+ searchError: undefined,
1145
+ loaderData: undefined,
1146
+ loadPromise: Promise.resolve(),
1147
+ routeContext: undefined,
1148
+ context: undefined,
1149
+ abortController: new AbortController(),
1150
+ fetchedAt: 0
1151
+ };
1152
+ return routeMatch;
1153
+ });
1993
1154
 
1994
- if (!route.routePath && (_route$childRoutes = route.childRoutes) != null && _route$childRoutes.length) {
1995
- return findMatchInRoutes([...foundRoutes, route], route.childRoutes);
1155
+ // Take each match and resolve its search params and context
1156
+ // This has to happen after the matches are created or found
1157
+ // so that we can use the parent match's search params and context
1158
+ matches.forEach((match, i) => {
1159
+ const parentMatch = matches[i - 1];
1160
+ const route = this.getRoute(match.routeId);
1161
+ const searchInfo = (() => {
1162
+ // Validate the search params and stabilize them
1163
+ const parentSearchInfo = {
1164
+ search: parentMatch?.search ?? locationSearch,
1165
+ routeSearch: parentMatch?.routeSearch ?? locationSearch
1166
+ };
1167
+ try {
1168
+ const validator = typeof route.options.validateSearch === 'object' ? route.options.validateSearch.parse : route.options.validateSearch;
1169
+ const routeSearch = validator?.(parentSearchInfo.search) ?? {};
1170
+ const search = {
1171
+ ...parentSearchInfo.search,
1172
+ ...routeSearch
1173
+ };
1174
+ return {
1175
+ routeSearch: replaceEqualDeep(match.routeSearch, routeSearch),
1176
+ search: replaceEqualDeep(match.search, search)
1177
+ };
1178
+ } catch (err) {
1179
+ match.searchError = new SearchParamError(err.message, {
1180
+ cause: err
1181
+ });
1182
+ if (opts?.throwOnError) {
1183
+ throw match.searchError;
1996
1184
  }
1185
+ return parentSearchInfo;
1186
+ }
1187
+ })();
1188
+ const contextInfo = (() => {
1189
+ try {
1190
+ const routeContext = route.options.getContext?.({
1191
+ parentContext: parentMatch?.routeContext ?? {},
1192
+ context: parentMatch?.context ?? this?.options.context ?? {},
1193
+ params: match.params,
1194
+ search: match.search
1195
+ }) || {};
1196
+ const context = {
1197
+ ...(parentMatch?.context ?? this?.options.context),
1198
+ ...routeContext
1199
+ };
1200
+ return {
1201
+ context,
1202
+ routeContext
1203
+ };
1204
+ } catch (err) {
1205
+ route.options.onError?.(err);
1206
+ throw err;
1207
+ }
1208
+ })();
1209
+ Object.assign(match, {
1210
+ ...searchInfo,
1211
+ ...contextInfo
1212
+ });
1213
+ });
1214
+ return matches;
1215
+ };
1216
+ loadMatches = async (resolvedMatches, opts) => {
1217
+ this.cleanMatches();
1218
+ let firstBadMatchIndex;
1997
1219
 
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
1220
+ // Check each match middleware to see if the route can be accessed
1221
+ try {
1222
+ await Promise.all(resolvedMatches.map(async (match, index) => {
1223
+ const route = this.getRoute(match.routeId);
1224
+ if (!opts?.preload) {
1225
+ // Update each match with its latest url data
1226
+ this.setRouteMatch(match.id, s => ({
1227
+ ...s,
1228
+ routeSearch: match.routeSearch,
1229
+ search: match.search,
1230
+ routeContext: match.routeContext,
1231
+ context: match.context,
1232
+ error: match.error,
1233
+ paramsError: match.paramsError,
1234
+ searchError: match.searchError,
1235
+ params: match.params
1236
+ }));
1237
+ }
1238
+ const handleError = (err, handler) => {
1239
+ firstBadMatchIndex = firstBadMatchIndex ?? index;
1240
+ handler = handler || route.options.onError;
1241
+ if (isRedirect(err)) {
1242
+ throw err;
1243
+ }
1244
+ try {
1245
+ handler?.(err);
1246
+ } catch (errorHandlerErr) {
1247
+ err = errorHandlerErr;
1248
+ if (isRedirect(errorHandlerErr)) {
1249
+ throw errorHandlerErr;
1250
+ }
1251
+ }
1252
+ this.setRouteMatch(match.id, s => ({
1253
+ ...s,
1254
+ error: err,
1255
+ status: 'error',
1256
+ updatedAt: Date.now()
1257
+ }));
1258
+ };
1259
+ if (match.paramsError) {
1260
+ handleError(match.paramsError, route.options.onParseParamsError);
1261
+ }
1262
+ if (match.searchError) {
1263
+ handleError(match.searchError, route.options.onValidateSearchError);
1264
+ }
1265
+ try {
1266
+ await route.options.beforeLoad?.({
1267
+ ...match,
1268
+ preload: !!opts?.preload
2003
1269
  });
2004
-
2005
- if (matchParams) {
2006
- let parsedParams;
2007
-
1270
+ } catch (err) {
1271
+ handleError(err, route.options.onBeforeLoadError);
1272
+ }
1273
+ }));
1274
+ } catch (err) {
1275
+ if (!opts?.preload) {
1276
+ this.navigate(err);
1277
+ }
1278
+ throw err;
1279
+ }
1280
+ const validResolvedMatches = resolvedMatches.slice(0, firstBadMatchIndex);
1281
+ const matchPromises = [];
1282
+ validResolvedMatches.forEach((match, index) => {
1283
+ matchPromises.push((async () => {
1284
+ const parentMatchPromise = matchPromises[index - 1];
1285
+ const route = this.getRoute(match.routeId);
1286
+ if (match.isFetching || match.status === 'success' && !this.getIsInvalid({
1287
+ matchId: match.id,
1288
+ preload: opts?.preload
1289
+ })) {
1290
+ return this.getRouteMatch(match.id)?.loadPromise;
1291
+ }
1292
+ const fetchedAt = Date.now();
1293
+ const checkLatest = () => {
1294
+ const latest = this.getRouteMatch(match.id);
1295
+ return latest && latest.fetchedAt !== fetchedAt ? latest.loadPromise : undefined;
1296
+ };
1297
+ const loadPromise = (async () => {
1298
+ let latestPromise;
1299
+ const componentsPromise = Promise.all(componentTypes.map(async type => {
1300
+ const component = route.options[type];
1301
+ if (component?.preload) {
1302
+ await component.preload();
1303
+ }
1304
+ }));
1305
+ const loaderPromise = route.options.loader?.({
1306
+ ...match,
1307
+ preload: !!opts?.preload,
1308
+ parentMatchPromise
1309
+ });
1310
+ const handleError = err => {
1311
+ if (isRedirect(err)) {
1312
+ if (!opts?.preload) {
1313
+ this.navigate(err);
1314
+ }
1315
+ return true;
1316
+ }
1317
+ return false;
1318
+ };
1319
+ try {
1320
+ const [_, loader] = await Promise.all([componentsPromise, loaderPromise]);
1321
+ if (latestPromise = checkLatest()) return await latestPromise;
1322
+ this.setRouteMatchData(match.id, () => loader, opts);
1323
+ } catch (err) {
1324
+ if (latestPromise = checkLatest()) return await latestPromise;
1325
+ if (handleError(err)) {
1326
+ return;
1327
+ }
1328
+ const errorHandler = route.options.onLoadError ?? route.options.onError;
1329
+ let caughtError = err;
2008
1330
  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;
1331
+ errorHandler?.(err);
1332
+ } catch (errorHandlerErr) {
1333
+ caughtError = errorHandlerErr;
1334
+ if (handleError(errorHandlerErr)) {
1335
+ return;
2015
1336
  }
2016
1337
  }
2017
-
2018
- params = _extends({}, params, parsedParams);
1338
+ this.setRouteMatch(match.id, s => ({
1339
+ ...s,
1340
+ error: caughtError,
1341
+ status: 'error',
1342
+ isFetching: false,
1343
+ updatedAt: Date.now()
1344
+ }));
2019
1345
  }
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
- }
1346
+ })();
1347
+ this.setRouteMatch(match.id, s => ({
1348
+ ...s,
1349
+ status: s.status !== 'success' ? 'pending' : s.status,
1350
+ isFetching: true,
1351
+ loadPromise,
1352
+ fetchedAt,
1353
+ invalid: false
1354
+ }));
1355
+ await loadPromise;
1356
+ })());
2070
1357
  });
2071
- router.notify();
2072
1358
  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
1359
+ };
1360
+ reload = () => {
1361
+ return this.navigate({
1362
+ fromCurrent: true,
1363
+ replace: true,
1364
+ search: true
2099
1365
  });
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;
1366
+ };
1367
+ resolvePath = (from, path) => {
1368
+ return resolvePath(this.basepath, from, cleanPath(path));
1369
+ };
1370
+ navigate = async ({
1371
+ from,
1372
+ to = '',
1373
+ search,
1374
+ hash,
1375
+ replace,
1376
+ params
1377
+ }) => {
2127
1378
  // If this link simply reloads the current route,
2128
1379
  // make sure it has a new key so it will trigger a data refresh
1380
+
2129
1381
  // If this `to` is a valid external URL, return
2130
1382
  // null for LinkUtils
2131
1383
  const toString = String(to);
2132
- const fromString = String(from);
1384
+ const fromString = typeof from === 'undefined' ? from : String(from);
2133
1385
  let isExternal;
2134
-
2135
1386
  try {
2136
- new URL("" + toString);
1387
+ new URL(`${toString}`);
2137
1388
  isExternal = true;
2138
1389
  } catch (e) {}
2139
-
2140
- invariant(!isExternal, 'Attempting to navigate to external url with router.navigate!');
2141
- return router.__.navigate({
1390
+ invariant(!isExternal, 'Attempting to navigate to external url with this.navigate!');
1391
+ return this.#commitLocation({
2142
1392
  from: fromString,
2143
1393
  to: toString,
2144
1394
  search,
@@ -2146,38 +1396,58 @@
2146
1396
  replace,
2147
1397
  params
2148
1398
  });
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
-
1399
+ };
1400
+ matchRoute = (location, opts) => {
1401
+ location = {
1402
+ ...location,
1403
+ to: location.to ? this.resolvePath(location.from ?? '', location.to) : undefined
1404
+ };
1405
+ const next = this.buildNext(location);
1406
+ if (opts?.pending && this.state.status !== 'pending') {
1407
+ return false;
1408
+ }
1409
+ const baseLocation = opts?.pending ? this.state.location : this.state.resolvedLocation;
1410
+ if (!baseLocation) {
1411
+ return false;
1412
+ }
1413
+ const match = matchPathname(this.basepath, baseLocation.pathname, {
1414
+ ...opts,
1415
+ to: next.pathname
1416
+ });
1417
+ if (!match) {
1418
+ return false;
1419
+ }
1420
+ if (opts?.includeSearch ?? true) {
1421
+ return partialDeepEqual(baseLocation.search, next.search) ? match : false;
1422
+ }
1423
+ return match;
1424
+ };
1425
+ buildLink = ({
1426
+ from,
1427
+ to = '.',
1428
+ search,
1429
+ params,
1430
+ hash,
1431
+ target,
1432
+ replace,
1433
+ activeOptions,
1434
+ preload,
1435
+ preloadDelay: userPreloadDelay,
1436
+ disabled
1437
+ }) => {
2169
1438
  // If this link simply reloads the current route,
2170
1439
  // make sure it has a new key so it will trigger a data refresh
1440
+
2171
1441
  // If this `to` is a valid external URL, return
2172
1442
  // null for LinkUtils
1443
+
2173
1444
  try {
2174
- new URL("" + to);
1445
+ new URL(`${to}`);
2175
1446
  return {
2176
1447
  type: 'external',
2177
1448
  href: to
2178
1449
  };
2179
1450
  } catch (e) {}
2180
-
2181
1451
  const nextOpts = {
2182
1452
  from,
2183
1453
  to,
@@ -2186,71 +1456,69 @@
2186
1456
  hash,
2187
1457
  replace
2188
1458
  };
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
1459
+ const next = this.buildNext(nextOpts);
1460
+ preload = preload ?? this.options.defaultPreload;
1461
+ const preloadDelay = userPreloadDelay ?? this.options.defaultPreloadDelay ?? 0;
2192
1462
 
2193
- const pathIsEqual = router.state.location.pathname === next.pathname;
2194
- const currentPathSplit = router.state.location.pathname.split('/');
1463
+ // Compare path/hash for matches
1464
+ const currentPathSplit = this.state.location.pathname.split('/');
2195
1465
  const nextPathSplit = next.pathname.split('/');
2196
1466
  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
1467
+ // Combine the matches based on user options
1468
+ const pathTest = activeOptions?.exact ? this.state.location.pathname === next.pathname : pathIsFuzzyEqual;
1469
+ const hashTest = activeOptions?.includeHash ? this.state.location.hash === next.hash : true;
1470
+ const searchTest = activeOptions?.includeSearch ?? true ? partialDeepEqual(this.state.location.search, next.search) : true;
2198
1471
 
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
1472
+ // The final "active" test
1473
+ const isActive = pathTest && hashTest && searchTest;
2203
1474
 
1475
+ // The click handler
2204
1476
  const handleClick = e => {
2205
1477
  if (!disabled && !isCtrlEvent(e) && !e.defaultPrevented && (!target || target === '_self') && e.button === 0) {
2206
1478
  e.preventDefault();
2207
1479
 
2208
- if (pathIsEqual && !search && !hash) {
2209
- router.invalidateRoute(nextOpts);
2210
- } // All is well? Navigate!)
2211
-
2212
-
2213
- router.__.navigate(nextOpts);
1480
+ // All is well? Navigate!
1481
+ this.#commitLocation(nextOpts);
2214
1482
  }
2215
- }; // The click handler
2216
-
1483
+ };
2217
1484
 
1485
+ // The click handler
2218
1486
  const handleFocus = e => {
2219
1487
  if (preload) {
2220
- router.preloadRoute(nextOpts, {
2221
- maxAge: userPreloadMaxAge,
2222
- gcMaxAge: userPreloadGcMaxAge
1488
+ this.preloadRoute(nextOpts).catch(err => {
1489
+ console.warn(err);
1490
+ console.warn('Error preloading route! ☝️');
2223
1491
  });
2224
1492
  }
2225
1493
  };
2226
-
1494
+ const handleTouchStart = e => {
1495
+ this.preloadRoute(nextOpts).catch(err => {
1496
+ console.warn(err);
1497
+ console.warn('Error preloading route! ☝️');
1498
+ });
1499
+ };
2227
1500
  const handleEnter = e => {
2228
1501
  const target = e.target || {};
2229
-
2230
1502
  if (preload) {
2231
1503
  if (target.preloadTimeout) {
2232
1504
  return;
2233
1505
  }
2234
-
2235
1506
  target.preloadTimeout = setTimeout(() => {
2236
1507
  target.preloadTimeout = null;
2237
- router.preloadRoute(nextOpts, {
2238
- maxAge: userPreloadMaxAge,
2239
- gcMaxAge: userPreloadGcMaxAge
1508
+ this.preloadRoute(nextOpts).catch(err => {
1509
+ console.warn(err);
1510
+ console.warn('Error preloading route! ☝️');
2240
1511
  });
2241
1512
  }, preloadDelay);
2242
1513
  }
2243
1514
  };
2244
-
2245
1515
  const handleLeave = e => {
2246
1516
  const target = e.target || {};
2247
-
2248
1517
  if (target.preloadTimeout) {
2249
1518
  clearTimeout(target.preloadTimeout);
2250
1519
  target.preloadTimeout = null;
2251
1520
  }
2252
1521
  };
2253
-
2254
1522
  return {
2255
1523
  type: 'internal',
2256
1524
  next,
@@ -2258,224 +1526,399 @@
2258
1526
  handleClick,
2259
1527
  handleEnter,
2260
1528
  handleLeave,
1529
+ handleTouchStart,
2261
1530
  isActive,
2262
1531
  disabled
2263
1532
  };
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);
1533
+ };
1534
+ dehydrate = () => {
1535
+ return {
1536
+ state: pick(this.state, ['location', 'status', 'lastUpdated'])
1537
+ };
1538
+ };
1539
+ hydrate = async __do_not_use_server_ctx => {
1540
+ let _ctx = __do_not_use_server_ctx;
1541
+ // Client hydrates from window
1542
+ if (typeof document !== 'undefined') {
1543
+ _ctx = window.__TSR_DEHYDRATED__;
1544
+ }
1545
+ invariant(_ctx, 'Expected to find a __TSR_DEHYDRATED__ property on window... but we did not. Did you forget to render <DehydrateRouter /> in your app?');
1546
+ const ctx = _ctx;
1547
+ this.dehydratedData = ctx.payload;
1548
+ this.options.hydrate?.(ctx.payload);
1549
+ this.__store.setState(s => {
2316
1550
  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
1551
+ ...s,
1552
+ ...ctx.router.state,
1553
+ resolvedLocation: ctx.router.state.location
2324
1554
  };
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
1555
+ });
1556
+ await this.load();
1557
+ return;
1558
+ };
1559
+ injectedHtml = [];
1560
+ injectHtml = async html => {
1561
+ this.injectedHtml.push(html);
1562
+ };
1563
+ dehydrateData = (key, getData) => {
1564
+ if (typeof document === 'undefined') {
1565
+ const strKey = typeof key === 'string' ? key : JSON.stringify(key);
1566
+ this.injectHtml(async () => {
1567
+ const id = `__TSR_DEHYDRATED__${strKey}`;
1568
+ const data = typeof getData === 'function' ? await getData() : getData;
1569
+ return `<script id='${id}' suppressHydrationWarning>window["__TSR_DEHYDRATED__${escapeJSON(strKey)}"] = ${JSON.stringify(data)}
1570
+ ;(() => {
1571
+ var el = document.getElementById('${id}')
1572
+ el.parentElement.removeChild(el)
1573
+ })()
1574
+ </script>`;
2345
1575
  });
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);
1576
+ return () => this.hydrateData(key);
1577
+ }
1578
+ return () => undefined;
1579
+ };
1580
+ hydrateData = key => {
1581
+ if (typeof document !== 'undefined') {
1582
+ const strKey = typeof key === 'string' ? key : JSON.stringify(key);
1583
+ return window[`__TSR_DEHYDRATED__${strKey}`];
1584
+ }
1585
+ return undefined;
1586
+ };
2351
1587
 
2352
- if (nextParams) {
2353
- toMatches.map(d => d.options.stringifyParams).filter(Boolean).forEach(fn => {
2354
- Object.assign({}, nextParams, fn(nextParams));
1588
+ // resolveMatchPromise = (matchId: string, key: string, value: any) => {
1589
+ // this.state.matches
1590
+ // .find((d) => d.id === matchId)
1591
+ // ?.__promisesByKey[key]?.resolve(value)
1592
+ // }
1593
+
1594
+ #buildRouteTree = routeTree => {
1595
+ this.routeTree = routeTree;
1596
+ this.routesById = {};
1597
+ this.routesByPath = {};
1598
+ this.flatRoutes = [];
1599
+ const recurseRoutes = routes => {
1600
+ routes.forEach((route, i) => {
1601
+ route.init({
1602
+ originalIndex: i,
1603
+ router: this
2355
1604
  });
1605
+ const existingRoute = this.routesById[route.id];
1606
+ invariant(!existingRoute, `Duplicate routes found with id: ${String(route.id)}`);
1607
+ this.routesById[route.id] = route;
1608
+ if (!route.isRoot && route.path) {
1609
+ const trimmedFullPath = trimPathRight(route.fullPath);
1610
+ if (!this.routesByPath[trimmedFullPath] || route.fullPath.endsWith('/')) {
1611
+ this.routesByPath[trimmedFullPath] = route;
1612
+ }
1613
+ }
1614
+ const children = route.children;
1615
+ if (children?.length) {
1616
+ recurseRoutes(children);
1617
+ }
1618
+ });
1619
+ };
1620
+ recurseRoutes([routeTree]);
1621
+ this.flatRoutes = Object.values(this.routesByPath).map((d, i) => {
1622
+ const trimmed = trimPath(d.fullPath);
1623
+ const parsed = parsePathname(trimmed);
1624
+ while (parsed.length > 1 && parsed[0]?.value === '/') {
1625
+ parsed.shift();
2356
1626
  }
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 : '';
1627
+ const score = parsed.map(d => {
1628
+ if (d.type === 'param') {
1629
+ return 0.5;
1630
+ }
1631
+ if (d.type === 'wildcard') {
1632
+ return 0.25;
1633
+ }
1634
+ return 1;
1635
+ });
2372
1636
  return {
2373
- pathname,
2374
- search,
2375
- searchStr,
2376
- state: router.location.state,
2377
- hash,
2378
- href: "" + pathname + searchStr + hash,
2379
- key: dest.key
1637
+ child: d,
1638
+ trimmed,
1639
+ parsed,
1640
+ index: i,
1641
+ score
2380
1642
  };
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';
1643
+ }).sort((a, b) => {
1644
+ let isIndex = a.trimmed === '/' ? 1 : b.trimmed === '/' ? -1 : 0;
1645
+ if (isIndex !== 0) return isIndex;
1646
+ const length = Math.min(a.score.length, b.score.length);
1647
+
1648
+ // Sort by length of score
1649
+ if (a.score.length !== b.score.length) {
1650
+ return b.score.length - a.score.length;
2389
1651
  }
2390
1652
 
2391
- const isSameUrl = router.__.parseLocation(history.location).href === next.href;
2392
-
2393
- if (isSameUrl && !next.key) {
2394
- nextAction = 'replace';
1653
+ // Sort by min available score
1654
+ for (let i = 0; i < length; i++) {
1655
+ if (a.score[i] !== b.score[i]) {
1656
+ return b.score[i] - a.score[i];
1657
+ }
2395
1658
  }
2396
1659
 
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
- });
1660
+ // Sort by min available parsed value
1661
+ for (let i = 0; i < length; i++) {
1662
+ if (a.parsed[i].value !== b.parsed[i].value) {
1663
+ return a.parsed[i].value > b.parsed[i].value ? 1 : -1;
1664
+ }
2413
1665
  }
2414
1666
 
2415
- router.navigationPromise = new Promise(resolve => {
2416
- const previousNavigationResolve = router.resolveNavigation;
1667
+ // Sort by length of trimmed full path
1668
+ if (a.trimmed !== b.trimmed) {
1669
+ return a.trimmed > b.trimmed ? 1 : -1;
1670
+ }
2417
1671
 
2418
- router.resolveNavigation = () => {
2419
- previousNavigationResolve();
2420
- resolve();
1672
+ // Sort by original index
1673
+ return a.index - b.index;
1674
+ }).map((d, i) => {
1675
+ d.child.rank = i;
1676
+ return d.child;
1677
+ });
1678
+ };
1679
+ #parseLocation = previousLocation => {
1680
+ let {
1681
+ pathname,
1682
+ search,
1683
+ hash,
1684
+ state
1685
+ } = this.history.location;
1686
+ const parsedSearch = this.options.parseSearch(search);
1687
+ return {
1688
+ pathname: pathname,
1689
+ searchStr: search,
1690
+ search: replaceEqualDeep(previousLocation?.search, parsedSearch),
1691
+ hash: hash.split('#').reverse()[0] ?? '',
1692
+ href: `${pathname}${search}${hash}`,
1693
+ state: state,
1694
+ key: state?.key || '__init__'
1695
+ };
1696
+ };
1697
+ #buildLocation = (dest = {}) => {
1698
+ dest.fromCurrent = dest.fromCurrent ?? dest.to === '';
1699
+ const fromPathname = dest.fromCurrent ? this.state.location.pathname : dest.from ?? this.state.location.pathname;
1700
+ let pathname = resolvePath(this.basepath ?? '/', fromPathname, `${dest.to ?? ''}`);
1701
+ const fromMatches = this.matchRoutes(this.state.location.pathname, this.state.location.search);
1702
+ const prevParams = {
1703
+ ...last(fromMatches)?.params
1704
+ };
1705
+ let nextParams = (dest.params ?? true) === true ? prevParams : functionalUpdate(dest.params, prevParams);
1706
+ if (nextParams) {
1707
+ dest.__matches?.map(d => this.getRoute(d.routeId).options.stringifyParams).filter(Boolean).forEach(fn => {
1708
+ nextParams = {
1709
+ ...nextParams,
1710
+ ...fn(nextParams)
2421
1711
  };
2422
1712
  });
2423
- return router.navigationPromise;
2424
1713
  }
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
- }
1714
+ pathname = interpolatePath(pathname, nextParams ?? {});
1715
+ const preSearchFilters = dest.__matches?.map(match => this.getRoute(match.routeId).options.preSearchFilters ?? []).flat().filter(Boolean) ?? [];
1716
+ const postSearchFilters = dest.__matches?.map(match => this.getRoute(match.routeId).options.postSearchFilters ?? []).flat().filter(Boolean) ?? [];
1717
+
1718
+ // Pre filters first
1719
+ const preFilteredSearch = preSearchFilters?.length ? preSearchFilters?.reduce((prev, next) => next(prev), this.state.location.search) : this.state.location.search;
1720
+
1721
+ // Then the link/navigate function
1722
+ const destSearch = dest.search === true ? preFilteredSearch // Preserve resolvedFrom true
1723
+ : dest.search ? functionalUpdate(dest.search, preFilteredSearch) ?? {} // Updater
1724
+ : preSearchFilters?.length ? preFilteredSearch // Preserve resolvedFrom filters
1725
+ : {};
1726
+
1727
+ // Then post filters
1728
+ const postFilteredSearch = postSearchFilters?.length ? postSearchFilters.reduce((prev, next) => next(prev), destSearch) : destSearch;
1729
+ const search = replaceEqualDeep(this.state.location.search, postFilteredSearch);
1730
+ const searchStr = this.options.stringifySearch(search);
1731
+ const hash = dest.hash === true ? this.state.location.hash : functionalUpdate(dest.hash, this.state.location.hash);
1732
+ const hashStr = hash ? `#${hash}` : '';
1733
+ const nextState = dest.state === true ? this.state.location.state : functionalUpdate(dest.state, this.state.location.state);
1734
+ return {
1735
+ pathname,
1736
+ search,
1737
+ searchStr,
1738
+ state: nextState,
1739
+ hash,
1740
+ href: this.history.createHref(`${pathname}${searchStr}${hashStr}`),
1741
+ key: dest.key
1742
+ };
1743
+ };
1744
+ #commitLocation = async location => {
1745
+ const next = this.buildNext(location);
1746
+ const id = '' + Date.now() + Math.random();
1747
+ if (this.navigateTimeout) clearTimeout(this.navigateTimeout);
1748
+ let nextAction = 'replace';
1749
+ if (!location.replace) {
1750
+ nextAction = 'push';
1751
+ }
1752
+ const isSameUrl = this.state.location.href === next.href;
1753
+ if (isSameUrl && !next.key) {
1754
+ nextAction = 'replace';
1755
+ }
1756
+ const href = `${next.pathname}${next.searchStr}${next.hash ? `#${next.hash}` : ''}`;
1757
+ this.history[nextAction === 'push' ? 'push' : 'replace'](href, {
1758
+ id,
1759
+ ...next.state
1760
+ });
1761
+ return this.latestLoadPromise;
1762
+ };
1763
+ getRouteMatch = id => {
1764
+ return this.state.matchesById[id];
1765
+ };
1766
+ setRouteMatch = (id, updater) => {
1767
+ this.__store.setState(prev => ({
1768
+ ...prev,
1769
+ matchesById: {
1770
+ ...prev.matchesById,
1771
+ [id]: updater(prev.matchesById[id])
1772
+ }
1773
+ }));
1774
+ };
1775
+ setRouteMatchData = (id, updater, opts) => {
1776
+ const match = this.getRouteMatch(id);
1777
+ if (!match) return;
1778
+ const route = this.getRoute(match.routeId);
1779
+ const updatedAt = opts?.updatedAt ?? Date.now();
1780
+ const preloadInvalidAt = updatedAt + (opts?.maxAge ?? route.options.preloadMaxAge ?? this.options.defaultPreloadMaxAge ?? 5000);
1781
+ const invalidAt = updatedAt + (opts?.maxAge ?? route.options.maxAge ?? this.options.defaultMaxAge ?? Infinity);
1782
+ this.setRouteMatch(id, s => ({
1783
+ ...s,
1784
+ error: undefined,
1785
+ status: 'success',
1786
+ isFetching: false,
1787
+ updatedAt: Date.now(),
1788
+ loaderData: functionalUpdate(updater, s.loaderData),
1789
+ preloadInvalidAt,
1790
+ invalidAt
1791
+ }));
1792
+ if (this.state.matches.find(d => d.id === id)) ;
1793
+ };
1794
+ invalidate = async opts => {
1795
+ if (opts?.matchId) {
1796
+ this.setRouteMatch(opts.matchId, s => ({
1797
+ ...s,
1798
+ invalid: true
1799
+ }));
1800
+ const matchIndex = this.state.matches.findIndex(d => d.id === opts.matchId);
1801
+ const childMatch = this.state.matches[matchIndex + 1];
1802
+ if (childMatch) {
1803
+ return this.invalidate({
1804
+ matchId: childMatch.id,
1805
+ reload: false
1806
+ });
1807
+ }
1808
+ } else {
1809
+ this.__store.batch(() => {
1810
+ Object.values(this.state.matchesById).forEach(match => {
1811
+ this.setRouteMatch(match.id, s => ({
1812
+ ...s,
1813
+ invalid: true
1814
+ }));
1815
+ });
1816
+ });
1817
+ }
1818
+ if (opts?.reload ?? true) {
1819
+ return this.reload();
1820
+ }
1821
+ };
1822
+ getIsInvalid = opts => {
1823
+ if (!opts?.matchId) {
1824
+ return !!this.state.matches.find(d => this.getIsInvalid({
1825
+ matchId: d.id,
1826
+ preload: opts?.preload
1827
+ }));
1828
+ }
1829
+ const match = this.getRouteMatch(opts?.matchId);
1830
+ if (!match) {
1831
+ return false;
1832
+ }
1833
+ const now = Date.now();
1834
+ return match.invalid || (opts?.preload ? match.preloadInvalidAt : match.invalidAt) < now;
1835
+ };
1836
+ }
2432
1837
 
2433
- function isCtrlEvent(e) {
2434
- return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
2435
- }
1838
+ // Detect if we're in the DOM
1839
+ const isServer = typeof window === 'undefined' || !window.document.createElement;
1840
+ function getInitialRouterState() {
1841
+ return {
1842
+ status: 'idle',
1843
+ isFetching: false,
1844
+ resolvedLocation: null,
1845
+ location: null,
1846
+ matchesById: {},
1847
+ matchIds: [],
1848
+ pendingMatchIds: [],
1849
+ matches: [],
1850
+ pendingMatches: [],
1851
+ lastUpdated: Date.now()
1852
+ };
1853
+ }
1854
+ function isCtrlEvent(e) {
1855
+ return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
1856
+ }
1857
+ function redirect(opts) {
1858
+ opts.isRedirect = true;
1859
+ return opts;
1860
+ }
1861
+ function isRedirect(obj) {
1862
+ return !!obj?.isRedirect;
1863
+ }
1864
+ class SearchParamError extends Error {}
1865
+ class PathParamError extends Error {}
1866
+ function escapeJSON(jsonString) {
1867
+ return jsonString.replace(/\\/g, '\\\\') // Escape backslashes
1868
+ .replace(/'/g, "\\'") // Escape single quotes
1869
+ .replace(/"/g, '\\"'); // Escape double quotes
1870
+ }
2436
1871
 
2437
- function cascadeLoaderData(matches) {
2438
- matches.forEach((match, index) => {
2439
- const parent = matches[index - 1];
1872
+ // A function that takes an import() argument which is a function and returns a new function that will
1873
+ // proxy arguments from the caller to the imported function, retaining all type
1874
+ // information along the way
1875
+ function lazyFn(fn, key) {
1876
+ return async (...args) => {
1877
+ const imported = await fn();
1878
+ return imported[key || 'default'](...args);
1879
+ };
1880
+ }
2440
1881
 
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 });
1882
+ exports.PathParamError = PathParamError;
1883
+ exports.RootRoute = RootRoute;
1884
+ exports.Route = Route;
1885
+ exports.Router = Router;
1886
+ exports.RouterContext = RouterContext;
1887
+ exports.SearchParamError = SearchParamError;
1888
+ exports.cleanPath = cleanPath;
1889
+ exports.componentTypes = componentTypes;
1890
+ exports.createBrowserHistory = createBrowserHistory;
1891
+ exports.createHashHistory = createHashHistory;
1892
+ exports.createMemoryHistory = createMemoryHistory;
1893
+ exports.decode = decode;
1894
+ exports.defaultParseSearch = defaultParseSearch;
1895
+ exports.defaultStringifySearch = defaultStringifySearch;
1896
+ exports.encode = encode;
1897
+ exports.functionalUpdate = functionalUpdate;
1898
+ exports.interpolatePath = interpolatePath;
1899
+ exports.invariant = invariant;
1900
+ exports.isPlainObject = isPlainObject;
1901
+ exports.isRedirect = isRedirect;
1902
+ exports.joinPaths = joinPaths;
1903
+ exports.last = last;
1904
+ exports.lazyFn = lazyFn;
1905
+ exports.matchByPath = matchByPath;
1906
+ exports.matchPathname = matchPathname;
1907
+ exports.parsePathname = parsePathname;
1908
+ exports.parseSearchWith = parseSearchWith;
1909
+ exports.partialDeepEqual = partialDeepEqual;
1910
+ exports.pick = pick;
1911
+ exports.redirect = redirect;
1912
+ exports.replaceEqualDeep = replaceEqualDeep;
1913
+ exports.resolvePath = resolvePath;
1914
+ exports.rootRouteId = rootRouteId;
1915
+ exports.stringifySearchWith = stringifySearchWith;
1916
+ exports.trimPath = trimPath;
1917
+ exports.trimPathLeft = trimPathLeft;
1918
+ exports.trimPathRight = trimPathRight;
1919
+ exports.warning = warning;
1920
+
1921
+ Object.defineProperty(exports, '__esModule', { value: true });
2479
1922
 
2480
1923
  }));
2481
1924
  //# sourceMappingURL=index.development.js.map