@tanstack/router-core 0.0.1-beta.16 → 0.0.1-beta.161

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