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

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