@tanstack/router-core 0.0.1-beta.15 → 0.0.1-beta.150

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