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

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 +1128 -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 +1431 -2089
  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 +1667 -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 +1503 -1023
  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,1385 @@
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
+ console.log(Object.values(next.matchesById).find(d => d.status === 'error'));
815
+ const matchesByIdChanged = prev.matchesById !== next.matchesById;
816
+ let matchesChanged;
817
+ let pendingMatchesChanged;
818
+ if (!matchesByIdChanged) {
819
+ matchesChanged = prev.matchIds.length !== next.matchIds.length || prev.matchIds.some((d, i) => d !== next.matchIds[i]);
820
+ pendingMatchesChanged = prev.pendingMatchIds.length !== next.pendingMatchIds.length || prev.pendingMatchIds.some((d, i) => d !== next.pendingMatchIds[i]);
1561
821
  }
1562
- });
1563
-
1564
- try {
1565
- await Promise.all([routeMatch.__.componentsPromise, routeMatch.__.dataPromise.catch(() => {})]);
1566
-
1567
- if (id !== routeMatch.__.latestId) {
1568
- return routeMatch.__.loadPromise;
822
+ if (matchesByIdChanged || matchesChanged) {
823
+ next.matches = next.matchIds.map(id => {
824
+ return next.matchesById[id];
825
+ });
1569
826
  }
1570
- } finally {
1571
- if (id !== routeMatch.__.latestId) {
1572
- return routeMatch.__.loadPromise;
827
+ if (matchesByIdChanged || pendingMatchesChanged) {
828
+ next.pendingMatches = next.pendingMatchIds.map(id => {
829
+ return next.matchesById[id];
830
+ });
1573
831
  }
1574
-
1575
- routeMatch.isFetching = false;
1576
-
1577
- routeMatch.__.notify();
1578
- }
832
+ next.isFetching = [...next.matches, ...next.pendingMatches].some(d => d.isFetching);
833
+ this.state = next;
834
+ },
835
+ defaultPriority: 'low'
1579
836
  });
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
- }
837
+ this.state = this.__store.state;
838
+ this.update(options);
839
+ const next = this.buildNext({
840
+ hash: true,
841
+ fromCurrent: true,
842
+ search: true,
843
+ state: true
1637
844
  });
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
845
+ if (this.state.location.href !== next.href) {
846
+ this.#commitLocation({
847
+ ...next,
848
+ replace: true
1716
849
  });
1717
850
  }
851
+ }
852
+ reset = () => {
853
+ this.__store.setState(s => Object.assign(s, getInitialRouterState()));
854
+ };
855
+ mount = () => {
856
+ // If the router matches are empty, start loading the matches
857
+ // if (!this.state.matches.length) {
858
+ this.safeLoad();
859
+ // }
860
+ };
1718
861
 
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);
862
+ update = opts => {
863
+ this.options = {
864
+ ...this.options,
865
+ ...opts,
866
+ context: {
867
+ ...this.options.context,
868
+ ...opts?.context
1777
869
  }
1778
870
  };
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;
871
+ if (!this.history || this.options.history && this.options.history !== this.history) {
872
+ if (this.#unsubHistory) {
873
+ this.#unsubHistory();
1789
874
  }
1790
-
1791
- router.location = router.__.parseLocation(router.history.location);
1792
- router.state.location = router.location;
875
+ this.history = this.options.history ?? (isServer ? createMemoryHistory() : createBrowserHistory());
876
+ const parsedLocation = this.#parseLocation();
877
+ this.__store.setState(s => ({
878
+ ...s,
879
+ resolvedLocation: parsedLocation,
880
+ location: parsedLocation
881
+ }));
882
+ this.#unsubHistory = this.history.listen(() => {
883
+ this.safeLoad({
884
+ next: this.#parseLocation(this.state.location)
885
+ });
886
+ });
1793
887
  }
1794
-
1795
- Object.assign(router.options, opts);
1796
888
  const {
1797
889
  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);
890
+ routeTree
891
+ } = this.options;
892
+ this.basepath = `/${trimPath(basepath ?? '') ?? ''}`;
893
+ if (routeTree && routeTree !== this.routeTree) {
894
+ this.#buildRouteTree(routeTree);
1805
895
  }
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();
896
+ return this;
897
+ };
898
+ buildNext = opts => {
899
+ const next = this.#buildLocation(opts);
900
+ const __matches = this.matchRoutes(next.pathname, next.search);
901
+ return this.#buildLocation({
902
+ ...opts,
903
+ __matches
1813
904
  });
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
905
+ };
906
+ cancelMatches = () => {
907
+ this.state.matches.forEach(match => {
908
+ this.cancelMatch(match.id);
1829
909
  });
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
- });
910
+ };
911
+ cancelMatch = id => {
912
+ this.getRouteMatch(id)?.abortController?.abort();
913
+ };
914
+ safeLoad = async opts => {
915
+ try {
916
+ return this.load(opts);
917
+ } catch (err) {
918
+ // Don't do anything
1845
919
  }
920
+ };
921
+ latestLoadPromise = Promise.resolve();
922
+ load = async opts => {
923
+ const promise = new Promise(async (resolve, reject) => {
924
+ let latestPromise;
925
+ const checkLatest = () => {
926
+ return this.latestLoadPromise !== promise ? this.latestLoadPromise : undefined;
927
+ };
1846
928
 
1847
- router.notify(); // Load the matches
1848
-
1849
- await router.loadMatches(matches);
929
+ // Cancel any pending matches
930
+ // this.cancelMatches()
931
+
932
+ let pendingMatches;
933
+ this.__store.batch(() => {
934
+ if (opts?.next) {
935
+ // Ingest the new location
936
+ this.__store.setState(s => ({
937
+ ...s,
938
+ location: opts.next
939
+ }));
940
+ }
1850
941
 
1851
- if (router.startedLoadingAt !== id) {
1852
- // Ignore side-effects of match loading
1853
- return router.navigationPromise;
1854
- }
942
+ // Match the routes
943
+ pendingMatches = this.matchRoutes(this.state.location.pathname, this.state.location.search, {
944
+ throwOnError: opts?.throwOnError,
945
+ debug: true
946
+ });
947
+ this.__store.setState(s => ({
948
+ ...s,
949
+ status: 'pending',
950
+ pendingMatchIds: pendingMatches.map(d => d.id),
951
+ matchesById: this.#mergeMatches(s.matchesById, pendingMatches)
952
+ }));
953
+ });
954
+ try {
955
+ // Load the matches
956
+ try {
957
+ await this.loadMatches(pendingMatches);
958
+ } catch (err) {
959
+ // swallow this error, since we'll display the
960
+ // errors on the route components
961
+ }
1855
962
 
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);
963
+ // Only apply the latest transition
964
+ if (latestPromise = checkLatest()) {
965
+ return latestPromise;
966
+ }
967
+ const prevLocation = this.state.resolvedLocation;
968
+ this.__store.setState(s => ({
969
+ ...s,
970
+ status: 'idle',
971
+ resolvedLocation: s.location,
972
+ matchIds: s.pendingMatchIds,
973
+ pendingMatchIds: []
974
+ }));
975
+ if (prevLocation.href !== this.state.location.href) {
976
+ this.options.onRouteChange?.();
977
+ }
978
+ resolve();
979
+ } catch (err) {
980
+ // Only apply the latest transition
981
+ if (latestPromise = checkLatest()) {
982
+ return latestPromise;
983
+ }
984
+ reject(err);
1864
985
  }
1865
986
  });
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
- };
987
+ this.latestLoadPromise = promise;
988
+ return this.latestLoadPromise;
989
+ };
990
+ #mergeMatches = (prevMatchesById, nextMatches) => {
991
+ const nextMatchesById = {
992
+ ...prevMatchesById
993
+ };
994
+ let hadNew = false;
995
+ nextMatches.forEach(match => {
996
+ if (!nextMatchesById[match.id]) {
997
+ hadNew = true;
998
+ nextMatchesById[match.id] = match;
1890
999
  }
1891
1000
  });
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;
1001
+ if (!hadNew) {
1002
+ return prevMatchesById;
1909
1003
  }
1910
-
1911
- matches.forEach(match => {
1912
- // Clear actions
1913
- if (match.action) {
1914
- match.action.current = undefined;
1915
- match.action.submissions = [];
1916
- }
1004
+ return nextMatchesById;
1005
+ };
1006
+ getRoute = id => {
1007
+ const route = this.routesById[id];
1008
+ invariant(route, `Route with id "${id}" not found`);
1009
+ return route;
1010
+ };
1011
+ preloadRoute = async (navigateOpts = this.state.location) => {
1012
+ const next = this.buildNext(navigateOpts);
1013
+ const matches = this.matchRoutes(next.pathname, next.search, {
1014
+ throwOnError: true
1015
+ });
1016
+ this.__store.setState(s => {
1017
+ return {
1018
+ ...s,
1019
+ matchesById: this.#mergeMatches(s.matchesById, matches)
1020
+ };
1917
1021
  });
1918
- router.state = _extends({}, router.state, {
1919
- location: router.location,
1920
- matches,
1921
- pending: undefined,
1922
- status: 'idle'
1022
+ await this.loadMatches(matches, {
1023
+ preload: true,
1024
+ maxAge: navigateOpts.maxAge
1923
1025
  });
1924
- router.notify();
1925
- router.resolveNavigation();
1926
- },
1927
- cleanMatchCache: () => {
1026
+ return matches;
1027
+ };
1028
+ cleanMatches = () => {
1928
1029
  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];
1030
+ const outdatedMatchIds = Object.values(this.state.matchesById).filter(match => {
1031
+ const route = this.getRoute(match.routeId);
1032
+ 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);
1033
+ }).map(d => d.id);
1034
+ if (outdatedMatchIds.length) {
1035
+ this.__store.setState(s => {
1036
+ const matchesById = {
1037
+ ...s.matchesById
1038
+ };
1039
+ outdatedMatchIds.forEach(id => {
1040
+ delete matchesById[id];
1041
+ });
1042
+ return {
1043
+ ...s,
1044
+ matchesById
1045
+ };
1046
+ });
1047
+ }
1048
+ };
1049
+ matchRoutes = (pathname, locationSearch, opts) => {
1050
+ let routeParams = {};
1051
+ let foundRoute = this.flatRoutes.find(route => {
1052
+ const matchedParams = matchPathname(this.basepath, trimPathRight(pathname), {
1053
+ to: route.fullPath,
1054
+ caseSensitive: route.options.caseSensitive ?? this.options.caseSensitive
1055
+ });
1056
+ if (matchedParams) {
1057
+ routeParams = matchedParams;
1058
+ return true;
1059
+ }
1060
+ return false;
1943
1061
  });
1944
- },
1945
- loadRoute: async function loadRoute(navigateOpts) {
1946
- if (navigateOpts === void 0) {
1947
- navigateOpts = router.location;
1062
+ let routeCursor = foundRoute || this.routesById['__root__'];
1063
+ let matchedRoutes = [routeCursor];
1064
+ // let includingLayouts = true
1065
+ while (routeCursor?.parentRoute) {
1066
+ routeCursor = routeCursor.parentRoute;
1067
+ if (routeCursor) matchedRoutes.unshift(routeCursor);
1948
1068
  }
1949
1069
 
1950
- const next = router.buildNext(navigateOpts);
1951
- const matches = router.matchRoutes(next.pathname, {
1952
- strictParseParams: true
1070
+ // Existing matches are matches that are already loaded along with
1071
+ // pending matches that are still loading
1072
+
1073
+ const parseErrors = matchedRoutes.map(route => {
1074
+ let parsedParamsError;
1075
+ if (route.options.parseParams) {
1076
+ try {
1077
+ const parsedParams = route.options.parseParams(routeParams);
1078
+ // Add the parsed params to the accumulated params bag
1079
+ Object.assign(routeParams, parsedParams);
1080
+ } catch (err) {
1081
+ parsedParamsError = new PathParamError(err.message, {
1082
+ cause: err
1083
+ });
1084
+ if (opts?.throwOnError) {
1085
+ throw parsedParamsError;
1086
+ }
1087
+ return parsedParamsError;
1088
+ }
1089
+ }
1090
+ return;
1953
1091
  });
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
- }
1092
+ const matches = matchedRoutes.map((route, index) => {
1093
+ const interpolatedPath = interpolatePath(route.path, routeParams);
1094
+ const key = route.options.key ? route.options.key({
1095
+ params: routeParams,
1096
+ search: locationSearch
1097
+ }) ?? '' : '';
1098
+ const stringifiedKey = key ? JSON.stringify(key) : '';
1099
+ const matchId = interpolatePath(route.id, routeParams, true) + stringifiedKey;
1100
+
1101
+ // Waste not, want not. If we already have a match for this route,
1102
+ // reuse it. This is important for layout routes, which might stick
1103
+ // around between navigation actions that only change leaf routes.
1104
+ const existingMatch = this.getRouteMatch(matchId);
1105
+ if (existingMatch) {
1106
+ return {
1107
+ ...existingMatch
1108
+ };
1109
+ }
1963
1110
 
1964
- const next = router.buildNext(navigateOpts);
1965
- const matches = router.matchRoutes(next.pathname, {
1966
- strictParseParams: true
1111
+ // Create a fresh route match
1112
+ const hasLoaders = !!(route.options.loader || componentTypes.some(d => route.options[d]?.preload));
1113
+ const routeMatch = {
1114
+ id: matchId,
1115
+ key: stringifiedKey,
1116
+ routeId: route.id,
1117
+ params: routeParams,
1118
+ pathname: joinPaths([this.basepath, interpolatedPath]),
1119
+ updatedAt: Date.now(),
1120
+ invalidAt: Infinity,
1121
+ preloadInvalidAt: Infinity,
1122
+ routeSearch: {},
1123
+ search: {},
1124
+ status: hasLoaders ? 'pending' : 'success',
1125
+ isFetching: false,
1126
+ invalid: false,
1127
+ error: undefined,
1128
+ paramsError: parseErrors[index],
1129
+ searchError: undefined,
1130
+ loaderData: undefined,
1131
+ loadPromise: Promise.resolve(),
1132
+ routeContext: undefined,
1133
+ context: undefined,
1134
+ abortController: new AbortController(),
1135
+ fetchedAt: 0
1136
+ };
1137
+ return routeMatch;
1967
1138
  });
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
1139
+
1140
+ // Take each match and resolve its search params and context
1141
+ // This has to happen after the matches are created or found
1142
+ // so that we can use the parent match's search params and context
1143
+ matches.forEach((match, i) => {
1144
+ const parentMatch = matches[i - 1];
1145
+ const route = this.getRoute(match.routeId);
1146
+ const searchInfo = (() => {
1147
+ // Validate the search params and stabilize them
1148
+ const parentSearchInfo = {
1149
+ search: parentMatch?.search ?? locationSearch,
1150
+ routeSearch: parentMatch?.routeSearch ?? locationSearch
1151
+ };
1152
+ try {
1153
+ const validator = typeof route.options.validateSearch === 'object' ? route.options.validateSearch.parse : route.options.validateSearch;
1154
+ const routeSearch = validator?.(parentSearchInfo.search) ?? {};
1155
+ const search = {
1156
+ ...parentSearchInfo.search,
1157
+ ...routeSearch
1158
+ };
1159
+ return {
1160
+ routeSearch: replaceEqualDeep(match.routeSearch, routeSearch),
1161
+ search: replaceEqualDeep(match.search, search)
1162
+ };
1163
+ } catch (err) {
1164
+ match.searchError = new SearchParamError(err.message, {
1165
+ cause: err
1166
+ });
1167
+ if (opts?.throwOnError) {
1168
+ throw match.searchError;
1169
+ }
1170
+ return parentSearchInfo;
1171
+ }
1172
+ })();
1173
+ Object.assign(match, {
1174
+ ...searchInfo
1175
+ });
1176
+ const contextInfo = (() => {
1177
+ try {
1178
+ const routeContext = route.options.getContext?.({
1179
+ parentContext: parentMatch?.routeContext ?? {},
1180
+ context: parentMatch?.context ?? this?.options.context ?? {},
1181
+ params: match.params,
1182
+ search: match.search
1183
+ }) || {};
1184
+ const context = {
1185
+ ...(parentMatch?.context ?? this?.options.context),
1186
+ ...routeContext
1187
+ };
1188
+ return {
1189
+ context,
1190
+ routeContext
1191
+ };
1192
+ } catch (err) {
1193
+ route.options.onError?.(err);
1194
+ throw err;
1195
+ }
1196
+ })();
1197
+ Object.assign(match, {
1198
+ ...contextInfo
1199
+ });
1972
1200
  });
1973
1201
  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;
1202
+ };
1203
+ loadMatches = async (resolvedMatches, opts) => {
1204
+ this.cleanMatches();
1205
+ if (!opts?.preload) {
1206
+ resolvedMatches.forEach(match => {
1207
+ // Update each match with its latest route data
1208
+ this.setRouteMatch(match.id, s => ({
1209
+ ...s,
1210
+ routeSearch: match.routeSearch,
1211
+ search: match.search,
1212
+ routeContext: match.routeContext,
1213
+ context: match.context,
1214
+ error: match.error,
1215
+ paramsError: match.paramsError,
1216
+ searchError: match.searchError,
1217
+ params: match.params
1218
+ }));
1219
+ });
1983
1220
  }
1221
+ let firstBadMatchIndex;
1984
1222
 
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);
1223
+ // Check each match middleware to see if the route can be accessed
1224
+ try {
1225
+ for (const [index, match] of resolvedMatches.entries()) {
1226
+ const route = this.getRoute(match.routeId);
1227
+ const handleError = (err, handler) => {
1228
+ firstBadMatchIndex = firstBadMatchIndex ?? index;
1229
+ handler = handler || route.options.onError;
1230
+ if (isRedirect(err)) {
1231
+ throw err;
2001
1232
  }
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
1233
+ try {
1234
+ handler?.(err);
1235
+ } catch (errorHandlerErr) {
1236
+ err = errorHandlerErr;
1237
+ if (isRedirect(errorHandlerErr)) {
1238
+ throw errorHandlerErr;
1239
+ }
1240
+ }
1241
+ console.log('set error');
1242
+ this.setRouteMatch(match.id, s => ({
1243
+ ...s,
1244
+ error: err,
1245
+ status: 'error',
1246
+ updatedAt: Date.now()
1247
+ }));
1248
+ };
1249
+ if (match.paramsError) {
1250
+ handleError(match.paramsError, route.options.onParseParamsError);
1251
+ }
1252
+ if (match.searchError) {
1253
+ handleError(match.searchError, route.options.onValidateSearchError);
1254
+ }
1255
+ let didError = false;
1256
+ try {
1257
+ await route.options.beforeLoad?.({
1258
+ ...match,
1259
+ preload: !!opts?.preload
2008
1260
  });
1261
+ } catch (err) {
1262
+ handleError(err, route.options.onBeforeLoadError);
1263
+ didError = true;
1264
+ }
2009
1265
 
2010
- if (matchParams) {
2011
- let parsedParams;
2012
-
1266
+ // If we errored, do not run the next matches' middleware
1267
+ if (didError) {
1268
+ break;
1269
+ }
1270
+ }
1271
+ } catch (err) {
1272
+ if (!opts?.preload) {
1273
+ this.navigate(err);
1274
+ }
1275
+ throw err;
1276
+ }
1277
+ const validResolvedMatches = resolvedMatches.slice(0, firstBadMatchIndex);
1278
+ const matchPromises = [];
1279
+ validResolvedMatches.forEach((match, index) => {
1280
+ matchPromises.push((async () => {
1281
+ const parentMatchPromise = matchPromises[index - 1];
1282
+ const route = this.getRoute(match.routeId);
1283
+ if (match.isFetching || match.status === 'success' && !this.getIsInvalid({
1284
+ matchId: match.id,
1285
+ preload: opts?.preload
1286
+ })) {
1287
+ return this.getRouteMatch(match.id)?.loadPromise;
1288
+ }
1289
+ const fetchedAt = Date.now();
1290
+ const checkLatest = () => {
1291
+ const latest = this.getRouteMatch(match.id);
1292
+ return latest && latest.fetchedAt !== fetchedAt ? latest.loadPromise : undefined;
1293
+ };
1294
+ const load = async () => {
1295
+ let latestPromise;
1296
+ const handleError = err => {
1297
+ if (isRedirect(err)) {
1298
+ if (!opts?.preload) {
1299
+ this.navigate(err);
1300
+ }
1301
+ return true;
1302
+ }
1303
+ return false;
1304
+ };
1305
+ try {
1306
+ const componentsPromise = Promise.all(componentTypes.map(async type => {
1307
+ const component = route.options[type];
1308
+ if (component?.preload) {
1309
+ await component.preload();
1310
+ }
1311
+ }));
1312
+ const loaderPromise = route.options.loader?.({
1313
+ ...match,
1314
+ preload: !!opts?.preload,
1315
+ parentMatchPromise
1316
+ });
1317
+ const [_, loader] = await Promise.all([componentsPromise, loaderPromise]);
1318
+ if (latestPromise = checkLatest()) return await latestPromise;
1319
+ this.setRouteMatchData(match.id, () => loader, opts);
1320
+ } catch (loaderError) {
1321
+ if (latestPromise = checkLatest()) return await latestPromise;
1322
+ handleError(loaderError);
1323
+ let error = loaderError;
2013
1324
  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;
1325
+ if (route.options.onLoadError) {
1326
+ route.options.onLoadError?.(loaderError);
1327
+ } else {
1328
+ route.options.onError?.(loaderError);
2020
1329
  }
1330
+ } catch (errorHandlerErr) {
1331
+ error = errorHandlerErr;
1332
+ handleError(error);
2021
1333
  }
2022
-
2023
- params = _extends({}, params, parsedParams);
1334
+ console.log('set error');
1335
+ this.setRouteMatch(match.id, s => ({
1336
+ ...s,
1337
+ error: error,
1338
+ status: 'error',
1339
+ isFetching: false,
1340
+ updatedAt: Date.now()
1341
+ }));
1342
+ console.log(this.getRouteMatch(match.id)?.status);
2024
1343
  }
2025
-
2026
- if (!!matchParams) {
2027
- foundRoutes = [...parentRoutes, route];
2028
- }
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])
2051
- });
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
- }
1344
+ };
1345
+ const loadPromise = load();
1346
+ this.setRouteMatch(match.id, s => ({
1347
+ ...s,
1348
+ status: s.status !== 'success' ? 'pending' : s.status,
1349
+ isFetching: true,
1350
+ loadPromise,
1351
+ fetchedAt,
1352
+ invalid: false
1353
+ }));
1354
+ await loadPromise;
1355
+ })());
2076
1356
  });
2077
- router.notify();
2078
1357
  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
1358
+ };
1359
+ reload = () => {
1360
+ return this.navigate({
1361
+ fromCurrent: true,
1362
+ replace: true,
1363
+ search: true
2105
1364
  });
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;
1365
+ };
1366
+ resolvePath = (from, path) => {
1367
+ return resolvePath(this.basepath, from, cleanPath(path));
1368
+ };
1369
+ navigate = async ({
1370
+ from,
1371
+ to = '',
1372
+ search,
1373
+ hash,
1374
+ replace,
1375
+ params
1376
+ }) => {
2133
1377
  // If this link simply reloads the current route,
2134
1378
  // make sure it has a new key so it will trigger a data refresh
1379
+
2135
1380
  // If this `to` is a valid external URL, return
2136
1381
  // null for LinkUtils
2137
1382
  const toString = String(to);
2138
- const fromString = String(from);
1383
+ const fromString = typeof from === 'undefined' ? from : String(from);
2139
1384
  let isExternal;
2140
-
2141
1385
  try {
2142
- new URL("" + toString);
1386
+ new URL(`${toString}`);
2143
1387
  isExternal = true;
2144
1388
  } catch (e) {}
2145
-
2146
- invariant(!isExternal, 'Attempting to navigate to external url with router.navigate!');
2147
- return router.__.navigate({
1389
+ invariant(!isExternal, 'Attempting to navigate to external url with this.navigate!');
1390
+ return this.#commitLocation({
2148
1391
  from: fromString,
2149
1392
  to: toString,
2150
1393
  search,
@@ -2152,111 +1395,131 @@
2152
1395
  replace,
2153
1396
  params
2154
1397
  });
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
-
1398
+ };
1399
+ matchRoute = (location, opts) => {
1400
+ location = {
1401
+ ...location,
1402
+ to: location.to ? this.resolvePath(location.from ?? '', location.to) : undefined
1403
+ };
1404
+ const next = this.buildNext(location);
1405
+ if (opts?.pending && this.state.status !== 'pending') {
1406
+ return false;
1407
+ }
1408
+ const baseLocation = opts?.pending ? this.state.location : this.state.resolvedLocation;
1409
+ if (!baseLocation) {
1410
+ return false;
1411
+ }
1412
+ const match = matchPathname(this.basepath, baseLocation.pathname, {
1413
+ ...opts,
1414
+ to: next.pathname
1415
+ });
1416
+ if (!match) {
1417
+ return false;
1418
+ }
1419
+ if (opts?.includeSearch ?? true) {
1420
+ return partialDeepEqual(baseLocation.search, next.search) ? match : false;
1421
+ }
1422
+ return match;
1423
+ };
1424
+ buildLink = ({
1425
+ from,
1426
+ to = '.',
1427
+ search,
1428
+ params,
1429
+ hash,
1430
+ target,
1431
+ replace,
1432
+ activeOptions,
1433
+ preload,
1434
+ preloadDelay: userPreloadDelay,
1435
+ disabled,
1436
+ state
1437
+ }) => {
2175
1438
  // If this link simply reloads the current route,
2176
1439
  // make sure it has a new key so it will trigger a data refresh
1440
+
2177
1441
  // If this `to` is a valid external URL, return
2178
1442
  // null for LinkUtils
1443
+
2179
1444
  try {
2180
- new URL("" + to);
1445
+ new URL(`${to}`);
2181
1446
  return {
2182
1447
  type: 'external',
2183
1448
  href: to
2184
1449
  };
2185
1450
  } catch (e) {}
2186
-
2187
1451
  const nextOpts = {
2188
1452
  from,
2189
1453
  to,
2190
1454
  search,
2191
1455
  params,
2192
1456
  hash,
2193
- replace
1457
+ replace,
1458
+ state
2194
1459
  };
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
1460
+ const next = this.buildNext(nextOpts);
1461
+ preload = preload ?? this.options.defaultPreload;
1462
+ const preloadDelay = userPreloadDelay ?? this.options.defaultPreloadDelay ?? 0;
2198
1463
 
2199
- const pathIsEqual = router.state.location.pathname === next.pathname;
2200
- const currentPathSplit = router.state.location.pathname.split('/');
1464
+ // Compare path/hash for matches
1465
+ const currentPathSplit = this.state.location.pathname.split('/');
2201
1466
  const nextPathSplit = next.pathname.split('/');
2202
1467
  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
1468
+ // Combine the matches based on user options
1469
+ const pathTest = activeOptions?.exact ? this.state.location.pathname === next.pathname : pathIsFuzzyEqual;
1470
+ const hashTest = activeOptions?.includeHash ? this.state.location.hash === next.hash : true;
1471
+ const searchTest = activeOptions?.includeSearch ?? true ? partialDeepEqual(this.state.location.search, next.search) : true;
2204
1472
 
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
1473
+ // The final "active" test
1474
+ const isActive = pathTest && hashTest && searchTest;
2209
1475
 
1476
+ // The click handler
2210
1477
  const handleClick = e => {
2211
1478
  if (!disabled && !isCtrlEvent(e) && !e.defaultPrevented && (!target || target === '_self') && e.button === 0) {
2212
1479
  e.preventDefault();
2213
1480
 
2214
- if (pathIsEqual && !search && !hash) {
2215
- router.invalidateRoute(nextOpts);
2216
- } // All is well? Navigate!)
2217
-
2218
-
2219
- router.__.navigate(nextOpts);
1481
+ // All is well? Navigate!
1482
+ this.#commitLocation(nextOpts);
2220
1483
  }
2221
- }; // The click handler
2222
-
1484
+ };
2223
1485
 
1486
+ // The click handler
2224
1487
  const handleFocus = e => {
2225
1488
  if (preload) {
2226
- router.preloadRoute(nextOpts, {
2227
- maxAge: userPreloadMaxAge,
2228
- gcMaxAge: userPreloadGcMaxAge
1489
+ this.preloadRoute(nextOpts).catch(err => {
1490
+ console.warn(err);
1491
+ console.warn('Error preloading route! ☝️');
2229
1492
  });
2230
1493
  }
2231
1494
  };
2232
-
1495
+ const handleTouchStart = e => {
1496
+ this.preloadRoute(nextOpts).catch(err => {
1497
+ console.warn(err);
1498
+ console.warn('Error preloading route! ☝️');
1499
+ });
1500
+ };
2233
1501
  const handleEnter = e => {
2234
1502
  const target = e.target || {};
2235
-
2236
1503
  if (preload) {
2237
1504
  if (target.preloadTimeout) {
2238
1505
  return;
2239
1506
  }
2240
-
2241
1507
  target.preloadTimeout = setTimeout(() => {
2242
1508
  target.preloadTimeout = null;
2243
- router.preloadRoute(nextOpts, {
2244
- maxAge: userPreloadMaxAge,
2245
- gcMaxAge: userPreloadGcMaxAge
1509
+ this.preloadRoute(nextOpts).catch(err => {
1510
+ console.warn(err);
1511
+ console.warn('Error preloading route! ☝️');
2246
1512
  });
2247
1513
  }, preloadDelay);
2248
1514
  }
2249
1515
  };
2250
-
2251
1516
  const handleLeave = e => {
2252
1517
  const target = e.target || {};
2253
-
2254
1518
  if (target.preloadTimeout) {
2255
1519
  clearTimeout(target.preloadTimeout);
2256
1520
  target.preloadTimeout = null;
2257
1521
  }
2258
1522
  };
2259
-
2260
1523
  return {
2261
1524
  type: 'internal',
2262
1525
  next,
@@ -2264,224 +1527,407 @@
2264
1527
  handleClick,
2265
1528
  handleEnter,
2266
1529
  handleLeave,
1530
+ handleTouchStart,
2267
1531
  isActive,
2268
1532
  disabled
2269
1533
  };
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);
1534
+ };
1535
+ dehydrate = () => {
1536
+ return {
1537
+ state: pick(this.state, ['location', 'status', 'lastUpdated'])
1538
+ };
1539
+ };
1540
+ hydrate = async __do_not_use_server_ctx => {
1541
+ let _ctx = __do_not_use_server_ctx;
1542
+ // Client hydrates from window
1543
+ if (typeof document !== 'undefined') {
1544
+ _ctx = window.__TSR_DEHYDRATED__;
1545
+ }
1546
+ invariant(_ctx, 'Expected to find a __TSR_DEHYDRATED__ property on window... but we did not. Did you forget to render <DehydrateRouter /> in your app?');
1547
+ const ctx = _ctx;
1548
+ this.dehydratedData = ctx.payload;
1549
+ this.options.hydrate?.(ctx.payload);
1550
+ const routerState = ctx.router.state;
1551
+ this.__store.setState(s => {
2322
1552
  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
1553
+ ...s,
1554
+ ...routerState,
1555
+ resolvedLocation: routerState.location
2330
1556
  };
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
1557
+ });
1558
+ await this.load();
1559
+ return;
1560
+ };
1561
+ injectedHtml = [];
1562
+ injectHtml = async html => {
1563
+ this.injectedHtml.push(html);
1564
+ };
1565
+ dehydrateData = (key, getData) => {
1566
+ if (typeof document === 'undefined') {
1567
+ const strKey = typeof key === 'string' ? key : JSON.stringify(key);
1568
+ this.injectHtml(async () => {
1569
+ const id = `__TSR_DEHYDRATED__${strKey}`;
1570
+ const data = typeof getData === 'function' ? await getData() : getData;
1571
+ return `<script id='${id}' suppressHydrationWarning>window["__TSR_DEHYDRATED__${escapeJSON(strKey)}"] = ${JSON.stringify(data)}
1572
+ ;(() => {
1573
+ var el = document.getElementById('${id}')
1574
+ el.parentElement.removeChild(el)
1575
+ })()
1576
+ </script>`;
2351
1577
  });
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);
1578
+ return () => this.hydrateData(key);
1579
+ }
1580
+ return () => undefined;
1581
+ };
1582
+ hydrateData = key => {
1583
+ if (typeof document !== 'undefined') {
1584
+ const strKey = typeof key === 'string' ? key : JSON.stringify(key);
1585
+ return window[`__TSR_DEHYDRATED__${strKey}`];
1586
+ }
1587
+ return undefined;
1588
+ };
2357
1589
 
2358
- if (nextParams) {
2359
- toMatches.map(d => d.options.stringifyParams).filter(Boolean).forEach(fn => {
2360
- Object.assign({}, nextParams, fn(nextParams));
1590
+ // resolveMatchPromise = (matchId: string, key: string, value: any) => {
1591
+ // this.state.matches
1592
+ // .find((d) => d.id === matchId)
1593
+ // ?.__promisesByKey[key]?.resolve(value)
1594
+ // }
1595
+
1596
+ #buildRouteTree = routeTree => {
1597
+ this.routeTree = routeTree;
1598
+ this.routesById = {};
1599
+ this.routesByPath = {};
1600
+ this.flatRoutes = [];
1601
+ const recurseRoutes = routes => {
1602
+ routes.forEach((route, i) => {
1603
+ route.init({
1604
+ originalIndex: i,
1605
+ router: this
2361
1606
  });
1607
+ const existingRoute = this.routesById[route.id];
1608
+ invariant(!existingRoute, `Duplicate routes found with id: ${String(route.id)}`);
1609
+ this.routesById[route.id] = route;
1610
+ if (!route.isRoot && route.path) {
1611
+ const trimmedFullPath = trimPathRight(route.fullPath);
1612
+ if (!this.routesByPath[trimmedFullPath] || route.fullPath.endsWith('/')) {
1613
+ this.routesByPath[trimmedFullPath] = route;
1614
+ }
1615
+ }
1616
+ const children = route.children;
1617
+ if (children?.length) {
1618
+ recurseRoutes(children);
1619
+ }
1620
+ });
1621
+ };
1622
+ recurseRoutes([routeTree]);
1623
+ this.flatRoutes = Object.values(this.routesByPath).map((d, i) => {
1624
+ const trimmed = trimPath(d.fullPath);
1625
+ const parsed = parsePathname(trimmed);
1626
+ while (parsed.length > 1 && parsed[0]?.value === '/') {
1627
+ parsed.shift();
2362
1628
  }
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 : '';
1629
+ const score = parsed.map(d => {
1630
+ if (d.type === 'param') {
1631
+ return 0.5;
1632
+ }
1633
+ if (d.type === 'wildcard') {
1634
+ return 0.25;
1635
+ }
1636
+ return 1;
1637
+ });
2378
1638
  return {
2379
- pathname,
2380
- search,
2381
- searchStr,
2382
- state: router.location.state,
2383
- hash,
2384
- href: "" + pathname + searchStr + hash,
2385
- key: dest.key
1639
+ child: d,
1640
+ trimmed,
1641
+ parsed,
1642
+ index: i,
1643
+ score
2386
1644
  };
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';
1645
+ }).sort((a, b) => {
1646
+ let isIndex = a.trimmed === '/' ? 1 : b.trimmed === '/' ? -1 : 0;
1647
+ if (isIndex !== 0) return isIndex;
1648
+ const length = Math.min(a.score.length, b.score.length);
1649
+
1650
+ // Sort by length of score
1651
+ if (a.score.length !== b.score.length) {
1652
+ return b.score.length - a.score.length;
2395
1653
  }
2396
1654
 
2397
- const isSameUrl = router.__.parseLocation(history.location).href === next.href;
2398
-
2399
- if (isSameUrl && !next.key) {
2400
- nextAction = 'replace';
1655
+ // Sort by min available score
1656
+ for (let i = 0; i < length; i++) {
1657
+ if (a.score[i] !== b.score[i]) {
1658
+ return b.score[i] - a.score[i];
1659
+ }
2401
1660
  }
2402
1661
 
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
- });
1662
+ // Sort by min available parsed value
1663
+ for (let i = 0; i < length; i++) {
1664
+ if (a.parsed[i].value !== b.parsed[i].value) {
1665
+ return a.parsed[i].value > b.parsed[i].value ? 1 : -1;
1666
+ }
2419
1667
  }
2420
1668
 
2421
- router.navigationPromise = new Promise(resolve => {
2422
- const previousNavigationResolve = router.resolveNavigation;
1669
+ // Sort by length of trimmed full path
1670
+ if (a.trimmed !== b.trimmed) {
1671
+ return a.trimmed > b.trimmed ? 1 : -1;
1672
+ }
2423
1673
 
2424
- router.resolveNavigation = () => {
2425
- previousNavigationResolve();
2426
- resolve();
1674
+ // Sort by original index
1675
+ return a.index - b.index;
1676
+ }).map((d, i) => {
1677
+ d.child.rank = i;
1678
+ return d.child;
1679
+ });
1680
+ };
1681
+ #parseLocation = previousLocation => {
1682
+ let {
1683
+ pathname,
1684
+ search,
1685
+ hash,
1686
+ state
1687
+ } = this.history.location;
1688
+ const parsedSearch = this.options.parseSearch(search);
1689
+ return {
1690
+ pathname: pathname,
1691
+ searchStr: search,
1692
+ search: replaceEqualDeep(previousLocation?.search, parsedSearch),
1693
+ hash: hash.split('#').reverse()[0] ?? '',
1694
+ href: `${pathname}${search}${hash}`,
1695
+ state: state,
1696
+ key: state?.key || '__init__'
1697
+ };
1698
+ };
1699
+ #buildLocation = (dest = {}) => {
1700
+ dest.fromCurrent = dest.fromCurrent ?? dest.to === '';
1701
+ const fromPathname = dest.fromCurrent ? this.state.location.pathname : dest.from ?? this.state.location.pathname;
1702
+ let pathname = resolvePath(this.basepath ?? '/', fromPathname, `${dest.to ?? ''}`);
1703
+ const fromMatches = this.matchRoutes(this.state.location.pathname, this.state.location.search);
1704
+ const prevParams = {
1705
+ ...last(fromMatches)?.params
1706
+ };
1707
+ let nextParams = (dest.params ?? true) === true ? prevParams : functionalUpdate(dest.params, prevParams);
1708
+ if (nextParams) {
1709
+ dest.__matches?.map(d => this.getRoute(d.routeId).options.stringifyParams).filter(Boolean).forEach(fn => {
1710
+ nextParams = {
1711
+ ...nextParams,
1712
+ ...fn(nextParams)
2427
1713
  };
2428
1714
  });
2429
- return router.navigationPromise;
2430
1715
  }
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
- }
1716
+ pathname = interpolatePath(pathname, nextParams ?? {});
1717
+ const preSearchFilters = dest.__matches?.map(match => this.getRoute(match.routeId).options.preSearchFilters ?? []).flat().filter(Boolean) ?? [];
1718
+ const postSearchFilters = dest.__matches?.map(match => this.getRoute(match.routeId).options.postSearchFilters ?? []).flat().filter(Boolean) ?? [];
1719
+
1720
+ // Pre filters first
1721
+ const preFilteredSearch = preSearchFilters?.length ? preSearchFilters?.reduce((prev, next) => next(prev), this.state.location.search) : this.state.location.search;
1722
+
1723
+ // Then the link/navigate function
1724
+ const destSearch = dest.search === true ? preFilteredSearch // Preserve resolvedFrom true
1725
+ : dest.search ? functionalUpdate(dest.search, preFilteredSearch) ?? {} // Updater
1726
+ : preSearchFilters?.length ? preFilteredSearch // Preserve resolvedFrom filters
1727
+ : {};
1728
+
1729
+ // Then post filters
1730
+ const postFilteredSearch = postSearchFilters?.length ? postSearchFilters.reduce((prev, next) => next(prev), destSearch) : destSearch;
1731
+ const search = replaceEqualDeep(this.state.location.search, postFilteredSearch);
1732
+ const searchStr = this.options.stringifySearch(search);
1733
+ const hash = dest.hash === true ? this.state.location.hash : functionalUpdate(dest.hash, this.state.location.hash);
1734
+ const hashStr = hash ? `#${hash}` : '';
1735
+ const nextState = dest.state === true ? this.state.location.state : functionalUpdate(dest.state, this.state.location.state);
1736
+ return {
1737
+ pathname,
1738
+ search,
1739
+ searchStr,
1740
+ state: nextState,
1741
+ hash,
1742
+ href: this.history.createHref(`${pathname}${searchStr}${hashStr}`),
1743
+ key: dest.key
1744
+ };
1745
+ };
1746
+ #commitLocation = async location => {
1747
+ const next = this.buildNext(location);
1748
+ const id = '' + Date.now() + Math.random();
1749
+ if (this.navigateTimeout) clearTimeout(this.navigateTimeout);
1750
+ let nextAction = 'replace';
1751
+ if (!location.replace) {
1752
+ nextAction = 'push';
1753
+ }
1754
+ const isSameUrl = this.state.location.href === next.href;
1755
+ if (isSameUrl && !next.key) {
1756
+ nextAction = 'replace';
1757
+ }
1758
+ const href = `${next.pathname}${next.searchStr}${next.hash ? `#${next.hash}` : ''}`;
1759
+ this.history[nextAction === 'push' ? 'push' : 'replace'](href, {
1760
+ id,
1761
+ ...next.state
1762
+ });
1763
+ return this.latestLoadPromise;
1764
+ };
1765
+ getRouteMatch = id => {
1766
+ return this.state.matchesById[id];
1767
+ };
1768
+ setRouteMatch = (id, updater) => {
1769
+ this.__store.setState(prev => {
1770
+ if (!prev.matchesById[id]) {
1771
+ console.warn(`No match found with id: ${id}`);
1772
+ }
1773
+ return {
1774
+ ...prev,
1775
+ matchesById: {
1776
+ ...prev.matchesById,
1777
+ [id]: updater(prev.matchesById[id])
1778
+ }
1779
+ };
1780
+ });
1781
+ };
1782
+ setRouteMatchData = (id, updater, opts) => {
1783
+ const match = this.getRouteMatch(id);
1784
+ if (!match) return;
1785
+ const route = this.getRoute(match.routeId);
1786
+ const updatedAt = opts?.updatedAt ?? Date.now();
1787
+ const preloadInvalidAt = updatedAt + (opts?.maxAge ?? route.options.preloadMaxAge ?? this.options.defaultPreloadMaxAge ?? 5000);
1788
+ const invalidAt = updatedAt + (opts?.maxAge ?? route.options.maxAge ?? this.options.defaultMaxAge ?? Infinity);
1789
+ console.log('set success');
1790
+ this.setRouteMatch(id, s => ({
1791
+ ...s,
1792
+ error: undefined,
1793
+ status: 'success',
1794
+ isFetching: false,
1795
+ updatedAt: Date.now(),
1796
+ loaderData: functionalUpdate(updater, s.loaderData),
1797
+ preloadInvalidAt,
1798
+ invalidAt
1799
+ }));
1800
+ if (this.state.matches.find(d => d.id === id)) ;
1801
+ };
1802
+ invalidate = async opts => {
1803
+ if (opts?.matchId) {
1804
+ this.setRouteMatch(opts.matchId, s => ({
1805
+ ...s,
1806
+ invalid: true
1807
+ }));
1808
+ const matchIndex = this.state.matches.findIndex(d => d.id === opts.matchId);
1809
+ const childMatch = this.state.matches[matchIndex + 1];
1810
+ if (childMatch) {
1811
+ return this.invalidate({
1812
+ matchId: childMatch.id,
1813
+ reload: false
1814
+ });
1815
+ }
1816
+ } else {
1817
+ this.__store.batch(() => {
1818
+ Object.values(this.state.matchesById).forEach(match => {
1819
+ this.setRouteMatch(match.id, s => ({
1820
+ ...s,
1821
+ invalid: true
1822
+ }));
1823
+ });
1824
+ });
1825
+ }
1826
+ if (opts?.reload ?? true) {
1827
+ return this.reload();
1828
+ }
1829
+ };
1830
+ getIsInvalid = opts => {
1831
+ if (!opts?.matchId) {
1832
+ return !!this.state.matches.find(d => this.getIsInvalid({
1833
+ matchId: d.id,
1834
+ preload: opts?.preload
1835
+ }));
1836
+ }
1837
+ const match = this.getRouteMatch(opts?.matchId);
1838
+ if (!match) {
1839
+ return false;
1840
+ }
1841
+ const now = Date.now();
1842
+ return match.invalid || (opts?.preload ? match.preloadInvalidAt : match.invalidAt) < now;
1843
+ };
1844
+ }
2438
1845
 
2439
- function isCtrlEvent(e) {
2440
- return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
2441
- }
1846
+ // Detect if we're in the DOM
1847
+ const isServer = typeof window === 'undefined' || !window.document.createElement;
1848
+ function getInitialRouterState() {
1849
+ return {
1850
+ status: 'idle',
1851
+ isFetching: false,
1852
+ resolvedLocation: null,
1853
+ location: null,
1854
+ matchesById: {},
1855
+ matchIds: [],
1856
+ pendingMatchIds: [],
1857
+ matches: [],
1858
+ pendingMatches: [],
1859
+ lastUpdated: Date.now()
1860
+ };
1861
+ }
1862
+ function isCtrlEvent(e) {
1863
+ return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
1864
+ }
1865
+ function redirect(opts) {
1866
+ opts.isRedirect = true;
1867
+ return opts;
1868
+ }
1869
+ function isRedirect(obj) {
1870
+ return !!obj?.isRedirect;
1871
+ }
1872
+ class SearchParamError extends Error {}
1873
+ class PathParamError extends Error {}
1874
+ function escapeJSON(jsonString) {
1875
+ return jsonString.replace(/\\/g, '\\\\') // Escape backslashes
1876
+ .replace(/'/g, "\\'") // Escape single quotes
1877
+ .replace(/"/g, '\\"'); // Escape double quotes
1878
+ }
2442
1879
 
2443
- function cascadeLoaderData(matches) {
2444
- matches.forEach((match, index) => {
2445
- const parent = matches[index - 1];
1880
+ // A function that takes an import() argument which is a function and returns a new function that will
1881
+ // proxy arguments from the caller to the imported function, retaining all type
1882
+ // information along the way
1883
+ function lazyFn(fn, key) {
1884
+ return async (...args) => {
1885
+ const imported = await fn();
1886
+ return imported[key || 'default'](...args);
1887
+ };
1888
+ }
2446
1889
 
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 });
1890
+ exports.FileRoute = FileRoute;
1891
+ exports.PathParamError = PathParamError;
1892
+ exports.RootRoute = RootRoute;
1893
+ exports.Route = Route;
1894
+ exports.Router = Router;
1895
+ exports.RouterContext = RouterContext;
1896
+ exports.SearchParamError = SearchParamError;
1897
+ exports.cleanPath = cleanPath;
1898
+ exports.componentTypes = componentTypes;
1899
+ exports.createBrowserHistory = createBrowserHistory;
1900
+ exports.createHashHistory = createHashHistory;
1901
+ exports.createMemoryHistory = createMemoryHistory;
1902
+ exports.decode = decode;
1903
+ exports.defaultParseSearch = defaultParseSearch;
1904
+ exports.defaultStringifySearch = defaultStringifySearch;
1905
+ exports.encode = encode;
1906
+ exports.functionalUpdate = functionalUpdate;
1907
+ exports.interpolatePath = interpolatePath;
1908
+ exports.invariant = invariant;
1909
+ exports.isPlainObject = isPlainObject;
1910
+ exports.isRedirect = isRedirect;
1911
+ exports.joinPaths = joinPaths;
1912
+ exports.last = last;
1913
+ exports.lazyFn = lazyFn;
1914
+ exports.matchByPath = matchByPath;
1915
+ exports.matchPathname = matchPathname;
1916
+ exports.parsePathname = parsePathname;
1917
+ exports.parseSearchWith = parseSearchWith;
1918
+ exports.partialDeepEqual = partialDeepEqual;
1919
+ exports.pick = pick;
1920
+ exports.redirect = redirect;
1921
+ exports.replaceEqualDeep = replaceEqualDeep;
1922
+ exports.resolvePath = resolvePath;
1923
+ exports.rootRouteId = rootRouteId;
1924
+ exports.stringifySearchWith = stringifySearchWith;
1925
+ exports.trimPath = trimPath;
1926
+ exports.trimPathLeft = trimPathLeft;
1927
+ exports.trimPathRight = trimPathRight;
1928
+ exports.warning = warning;
1929
+
1930
+ Object.defineProperty(exports, '__esModule', { value: true });
2485
1931
 
2486
1932
  }));
2487
1933
  //# sourceMappingURL=index.development.js.map