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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/LICENSE +21 -0
  2. package/build/cjs/fileRoute.js +29 -0
  3. package/build/cjs/fileRoute.js.map +1 -0
  4. package/build/cjs/history.js +226 -0
  5. package/build/cjs/history.js.map +1 -0
  6. package/build/cjs/index.js +78 -0
  7. package/build/cjs/{packages/router-core/src/index.js.map → index.js.map} +1 -1
  8. package/build/cjs/{packages/router-core/src/path.js → path.js} +45 -56
  9. package/build/cjs/path.js.map +1 -0
  10. package/build/cjs/{packages/router-core/src/qss.js → qss.js} +10 -16
  11. package/build/cjs/qss.js.map +1 -0
  12. package/build/cjs/route.js +103 -0
  13. package/build/cjs/route.js.map +1 -0
  14. package/build/cjs/router.js +1102 -0
  15. package/build/cjs/router.js.map +1 -0
  16. package/build/cjs/{packages/router-core/src/searchParams.js → searchParams.js} +11 -13
  17. package/build/cjs/searchParams.js.map +1 -0
  18. package/build/cjs/{packages/router-core/src/utils.js → utils.js} +54 -64
  19. package/build/cjs/utils.js.map +1 -0
  20. package/build/esm/index.js +1403 -2096
  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 +603 -422
  25. package/build/umd/index.development.js +1629 -2218
  26. package/build/umd/index.development.js.map +1 -1
  27. package/build/umd/index.production.js +13 -2
  28. package/build/umd/index.production.js.map +1 -1
  29. package/package.json +11 -7
  30. package/src/fileRoute.ts +122 -0
  31. package/src/history.ts +292 -0
  32. package/src/index.ts +3 -10
  33. package/src/link.ts +118 -113
  34. package/src/path.ts +37 -17
  35. package/src/qss.ts +1 -2
  36. package/src/route.ts +891 -218
  37. package/src/routeInfo.ts +121 -197
  38. package/src/router.ts +1481 -1018
  39. package/src/searchParams.ts +1 -1
  40. package/src/utils.ts +80 -49
  41. package/build/cjs/_virtual/_rollupPluginBabelHelpers.js +0 -33
  42. package/build/cjs/_virtual/_rollupPluginBabelHelpers.js.map +0 -1
  43. package/build/cjs/node_modules/@babel/runtime/helpers/esm/extends.js +0 -33
  44. package/build/cjs/node_modules/@babel/runtime/helpers/esm/extends.js.map +0 -1
  45. package/build/cjs/node_modules/history/index.js +0 -815
  46. package/build/cjs/node_modules/history/index.js.map +0 -1
  47. package/build/cjs/node_modules/tiny-invariant/dist/esm/tiny-invariant.js +0 -30
  48. package/build/cjs/node_modules/tiny-invariant/dist/esm/tiny-invariant.js.map +0 -1
  49. package/build/cjs/packages/router-core/src/index.js +0 -58
  50. package/build/cjs/packages/router-core/src/path.js.map +0 -1
  51. package/build/cjs/packages/router-core/src/qss.js.map +0 -1
  52. package/build/cjs/packages/router-core/src/route.js +0 -147
  53. package/build/cjs/packages/router-core/src/route.js.map +0 -1
  54. package/build/cjs/packages/router-core/src/routeConfig.js +0 -69
  55. package/build/cjs/packages/router-core/src/routeConfig.js.map +0 -1
  56. package/build/cjs/packages/router-core/src/routeMatch.js +0 -226
  57. package/build/cjs/packages/router-core/src/routeMatch.js.map +0 -1
  58. package/build/cjs/packages/router-core/src/router.js +0 -832
  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 -489
  64. package/src/routeMatch.ts +0 -312
@@ -1,5 +1,5 @@
1
1
  /**
2
- * router-core
2
+ * @tanstack/router-core/src/index.ts
3
3
  *
4
4
  * Copyright (c) TanStack
5
5
  *
@@ -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;
184
- }
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);
231
- }
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);
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;
249
46
  }
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);
47
+ while (queue.length) {
48
+ queue.shift()?.();
258
49
  }
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);
50
+ if (!opts.listener) {
51
+ onUpdate();
268
52
  }
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
- }
765
- };
766
- }
767
-
768
- function createKey() {
769
- return Math.random().toString(36).substr(2, 8);
770
- }
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
-
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;
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
+ });
788
206
  }
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
- }
816
- }
817
-
818
- return parsedPath;
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
216
+ };
819
217
  }
820
218
 
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);
219
+ // Thanks co-pilot!
220
+ function createRandomKey() {
221
+ return (Math.random() + 1).toString(36).substring(7);
833
222
  }
834
223
 
835
- // type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
836
- // k: infer I,
837
- // ) => any
838
- // ? I
839
- // : never
224
+ function last(arr) {
225
+ return arr[arr.length - 1];
226
+ }
227
+ function isFunction(d) {
228
+ return typeof d === 'function';
229
+ }
230
+ function functionalUpdate(updater, previous) {
231
+ if (isFunction(updater)) {
232
+ return updater(previous);
233
+ }
234
+ return updater;
235
+ }
236
+ function pick(parent, keys) {
237
+ return keys.reduce((obj, key) => {
238
+ obj[key] = parent[key];
239
+ return obj;
240
+ }, {});
241
+ }
840
242
 
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,488 +478,161 @@ 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
- // @ts-nocheck
1136
- // qss has been slightly modified and inlined here for our use cases (and compression's sake). We've included it as a hard dependency for MIT license attribution.
1137
- function encode(obj, pfx) {
1138
- var k,
1139
- i,
1140
- tmp,
1141
- str = '';
1142
-
1143
- for (k in obj) {
1144
- if ((tmp = obj[k]) !== void 0) {
1145
- if (Array.isArray(tmp)) {
1146
- for (i = 0; i < tmp.length; i++) {
1147
- str && (str += '&');
1148
- str += encodeURIComponent(k) + '=' + encodeURIComponent(tmp[i]);
1149
- }
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
502
+ // @ts-nocheck
1481
503
 
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.
1482
505
 
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
- });
506
+ function encode(obj, pfx) {
507
+ var k,
508
+ i,
509
+ tmp,
510
+ str = '';
511
+ for (k in obj) {
512
+ if ((tmp = obj[k]) !== void 0) {
513
+ if (Array.isArray(tmp)) {
514
+ for (i = 0; i < tmp.length; i++) {
515
+ str && (str += '&');
516
+ str += encodeURIComponent(k) + '=' + encodeURIComponent(tmp[i]);
517
+ }
518
+ } else {
519
+ str && (str += '&');
520
+ str += encodeURIComponent(k) + '=' + encodeURIComponent(tmp);
1488
521
  }
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
- const data = await routeMatch.options.loader({
1528
- params: routeMatch.params,
1529
- search: routeMatch.routeSearch,
1530
- signal: routeMatch.__.abortController.signal
1531
- });
1532
-
1533
- if (id !== routeMatch.__.latestId) {
1534
- return routeMatch.__.loadPromise;
1535
- }
1536
-
1537
- routeMatch.routeLoaderData = replaceEqualDeep(routeMatch.routeLoaderData, data);
1538
- }
1539
-
1540
- routeMatch.error = undefined;
1541
- routeMatch.status = 'success';
1542
- routeMatch.updatedAt = Date.now();
1543
- routeMatch.invalidAt = routeMatch.updatedAt + ((_ref = (_ref2 = (_opts$maxAge = opts == null ? void 0 : opts.maxAge) != null ? _opts$maxAge : routeMatch.options.loaderMaxAge) != null ? _ref2 : router.options.defaultLoaderMaxAge) != null ? _ref : 0);
1544
- } catch (err) {
1545
- if (id !== routeMatch.__.latestId) {
1546
- return routeMatch.__.loadPromise;
1547
- }
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
+ }
1548
549
 
1549
- if (process.env.NODE_ENV !== 'production') {
1550
- console.error(err);
1551
- }
550
+ const rootRouteId = '__root__';
1552
551
 
1553
- routeMatch.error = err;
1554
- routeMatch.status = 'error';
1555
- routeMatch.updatedAt = Date.now();
1556
- }
1557
- });
552
+ // | ParseParamsObj<TPath, TParams>
1558
553
 
1559
- try {
1560
- await Promise.all([routeMatch.__.componentsPromise, routeMatch.__.dataPromise]);
554
+ // The parse type here allows a zod schema to be passed directly to the validator
1561
555
 
1562
- if (id !== routeMatch.__.latestId) {
1563
- return routeMatch.__.loadPromise;
1564
- }
1565
- } finally {
1566
- if (id !== routeMatch.__.latestId) {
1567
- return routeMatch.__.loadPromise;
1568
- }
556
+ class Route {
557
+ // Set up in this.init()
1569
558
 
1570
- routeMatch.isFetching = false;
559
+ // customId!: TCustomId
1571
560
 
1572
- routeMatch.__.notify();
1573
- }
1574
- });
1575
- await routeMatch.__.loadPromise;
561
+ // Optional
1576
562
 
1577
- if (id !== routeMatch.__.latestId) {
1578
- return routeMatch.__.loadPromise;
1579
- }
563
+ constructor(options) {
564
+ this.options = options || {};
565
+ this.isRoot = !options?.getParentRoute;
566
+ Route.__onInit(this);
567
+ }
568
+ init = opts => {
569
+ this.originalIndex = opts.originalIndex;
570
+ this.router = opts.router;
571
+ const options = this.options;
572
+ const isRoot = !options?.path && !options?.id;
573
+ this.parentRoute = this.options?.getParentRoute?.();
574
+ if (isRoot) {
575
+ this.path = rootRouteId;
576
+ } else {
577
+ invariant(this.parentRoute, `Child Route instances must pass a 'getParentRoute: () => ParentRoute' option that returns a Route instance.`);
578
+ }
579
+ let path = isRoot ? rootRouteId : options.path;
1580
580
 
1581
- delete routeMatch.__.loadPromise;
581
+ // If the path is anything other than an index path, trim it up
582
+ if (path && path !== '/') {
583
+ path = trimPath(path);
1582
584
  }
1583
- });
585
+ const customId = options?.id || path;
1584
586
 
1585
- if (!routeMatch.hasLoaders()) {
1586
- routeMatch.status = 'success';
587
+ // Strip the parentId prefix from the first level of children
588
+ let id = isRoot ? rootRouteId : joinPaths([this.parentRoute.id === rootRouteId ? '' : this.parentRoute.id, customId]);
589
+ if (path === rootRouteId) {
590
+ path = '/';
591
+ }
592
+ if (id !== rootRouteId) {
593
+ id = joinPaths(['/', id]);
594
+ }
595
+ const fullPath = id === rootRouteId ? '/' : joinPaths([this.parentRoute.fullPath, path]);
596
+ this.path = path;
597
+ this.id = id;
598
+ // this.customId = customId as TCustomId
599
+ this.fullPath = fullPath;
600
+ this.to = fullPath;
601
+ };
602
+ addChildren = children => {
603
+ this.children = children;
604
+ return this;
605
+ };
606
+ update = options => {
607
+ Object.assign(this.options, options);
608
+ return this;
609
+ };
610
+ static __onInit = route => {
611
+ // This is a dummy static method that should get
612
+ // replaced by a framework specific implementation if necessary
613
+ };
614
+ }
615
+ class RouterContext {
616
+ constructor() {}
617
+ createRootRoute = options => {
618
+ return new RootRoute(options);
619
+ };
620
+ }
621
+ class RootRoute extends Route {
622
+ constructor(options) {
623
+ super(options);
1587
624
  }
625
+ }
1588
626
 
1589
- return routeMatch;
627
+ class FileRoute {
628
+ constructor(path) {
629
+ this.path = path;
630
+ }
631
+ createRoute = options => {
632
+ const route = new Route(options);
633
+ route.isRoot = false;
634
+ return route;
635
+ };
1590
636
  }
1591
637
 
1592
638
  const defaultParseSearch = parseSearchWith(JSON.parse);
@@ -1596,853 +642,1114 @@ function parseSearchWith(parser) {
1596
642
  if (searchStr.substring(0, 1) === '?') {
1597
643
  searchStr = searchStr.substring(1);
1598
644
  }
645
+ let query = decode(searchStr);
1599
646
 
1600
- let query = decode(searchStr); // Try to parse any query params that might be json
1601
-
647
+ // Try to parse any query params that might be json
1602
648
  for (let key in query) {
1603
649
  const value = query[key];
1604
-
1605
650
  if (typeof value === 'string') {
1606
651
  try {
1607
652
  query[key] = parser(value);
1608
- } catch (err) {//
653
+ } catch (err) {
654
+ //
1609
655
  }
1610
656
  }
1611
657
  }
1612
-
1613
658
  return query;
1614
659
  };
1615
660
  }
1616
661
  function stringifySearchWith(stringify) {
1617
662
  return search => {
1618
- search = _extends({}, search);
1619
-
663
+ search = {
664
+ ...search
665
+ };
1620
666
  if (search) {
1621
667
  Object.keys(search).forEach(key => {
1622
668
  const val = search[key];
1623
-
1624
669
  if (typeof val === 'undefined' || val === undefined) {
1625
670
  delete search[key];
1626
671
  } else if (val && typeof val === 'object' && val !== null) {
1627
672
  try {
1628
673
  search[key] = stringify(val);
1629
- } catch (err) {// silent
674
+ } catch (err) {
675
+ // silent
1630
676
  }
1631
677
  }
1632
678
  });
1633
679
  }
1634
-
1635
680
  const searchStr = encode(search).toString();
1636
- return searchStr ? "?" + searchStr : '';
1637
- };
1638
- }
1639
-
1640
- var _window$document;
1641
- // Detect if we're in the DOM
1642
- const isServer = typeof window === 'undefined' || !((_window$document = window.document) != null && _window$document.createElement); // This is the default history object if none is defined
1643
-
1644
- const createDefaultHistory = () => isServer ? createMemoryHistory() : createBrowserHistory();
1645
-
1646
- function getInitialRouterState() {
1647
- return {
1648
- status: 'idle',
1649
- location: null,
1650
- matches: [],
1651
- actions: {},
1652
- loaders: {},
1653
- lastUpdated: Date.now(),
1654
- isFetching: false,
1655
- isPreloading: false
681
+ return searchStr ? `?${searchStr}` : '';
1656
682
  };
1657
683
  }
1658
684
 
1659
- function createRouter(userOptions) {
1660
- var _userOptions$stringif, _userOptions$parseSea;
1661
-
1662
- const history = (userOptions == null ? void 0 : userOptions.history) || createDefaultHistory();
1663
-
1664
- const originalOptions = _extends({
1665
- defaultLoaderGcMaxAge: 5 * 60 * 1000,
1666
- defaultLoaderMaxAge: 0,
1667
- defaultPreloadMaxAge: 2000,
1668
- defaultPreloadDelay: 50
1669
- }, userOptions, {
1670
- stringifySearch: (_userOptions$stringif = userOptions == null ? void 0 : userOptions.stringifySearch) != null ? _userOptions$stringif : defaultStringifySearch,
1671
- parseSearch: (_userOptions$parseSea = userOptions == null ? void 0 : userOptions.parseSearch) != null ? _userOptions$parseSea : defaultParseSearch
1672
- });
1673
-
1674
- let router = {
1675
- history,
1676
- options: originalOptions,
1677
- listeners: [],
1678
- // Resolved after construction
1679
- basepath: '',
1680
- routeTree: undefined,
1681
- routesById: {},
1682
- location: undefined,
1683
- allRouteInfo: undefined,
1684
- //
1685
- navigationPromise: Promise.resolve(),
1686
- resolveNavigation: () => {},
1687
- matchCache: {},
1688
- state: getInitialRouterState(),
1689
- reset: () => {
1690
- router.state = getInitialRouterState();
1691
- router.notify();
1692
- },
1693
- startedLoadingAt: Date.now(),
1694
- subscribe: listener => {
1695
- router.listeners.push(listener);
1696
- return () => {
1697
- router.listeners = router.listeners.filter(x => x !== listener);
1698
- };
1699
- },
1700
- getRoute: id => {
1701
- return router.routesById[id];
1702
- },
1703
- notify: () => {
1704
- const isFetching = router.state.status === 'loading' || router.state.matches.some(d => d.isFetching);
1705
- const isPreloading = Object.values(router.matchCache).some(d => d.match.isFetching && !router.state.matches.find(dd => dd.matchId === d.match.matchId));
1706
-
1707
- if (router.state.isFetching !== isFetching || router.state.isPreloading !== isPreloading) {
1708
- router.state = _extends({}, router.state, {
1709
- isFetching,
1710
- isPreloading
1711
- });
1712
- }
1713
-
1714
- cascadeLoaderData(router.state.matches);
1715
- router.listeners.forEach(listener => listener(router));
1716
- },
1717
- dehydrateState: () => {
1718
- return _extends({}, pick(router.state, ['status', 'location', 'lastUpdated']), {
1719
- matches: router.state.matches.map(match => pick(match, ['matchId', 'status', 'routeLoaderData', 'loaderData', 'isInvalid', 'invalidAt']))
1720
- });
1721
- },
1722
- hydrateState: dehydratedState => {
1723
- // Match the routes
1724
- const matches = router.matchRoutes(router.location.pathname, {
1725
- strictParseParams: true
1726
- });
1727
- matches.forEach((match, index) => {
1728
- const dehydratedMatch = dehydratedState.matches[index];
1729
- invariant(dehydratedMatch, 'Oh no! Dehydrated route matches did not match the active state of the router 😬');
1730
- Object.assign(match, dehydratedMatch);
1731
- });
1732
- matches.forEach(match => match.__.validate());
1733
- router.state = _extends({}, router.state, dehydratedState, {
1734
- matches
1735
- });
1736
- },
1737
- mount: () => {
1738
- const next = router.__.buildLocation({
1739
- to: '.',
1740
- search: true,
1741
- hash: true
1742
- }); // If the current location isn't updated, trigger a navigation
1743
- // to the current location. Otherwise, load the current location.
1744
-
685
+ //
1745
686
 
1746
- if (next.href !== router.location.href) {
1747
- router.__.commitLocation(next, true);
1748
- }
1749
-
1750
- if (!router.state.matches.length) {
1751
- router.load();
1752
- }
1753
-
1754
- const unsub = router.history.listen(event => {
1755
- router.load(router.__.parseLocation(event.location, router.location));
1756
- }); // addEventListener does not exist in React Native, but window does
1757
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
1758
-
1759
- if (!isServer && window.addEventListener) {
1760
- // Listen to visibillitychange and focus
1761
- window.addEventListener('visibilitychange', router.onFocus, false);
1762
- window.addEventListener('focus', router.onFocus, false);
1763
- }
1764
-
1765
- return () => {
1766
- unsub();
687
+ const componentTypes = ['component', 'errorComponent', 'pendingComponent'];
688
+ class Router {
689
+ #unsubHistory;
690
+ constructor(options) {
691
+ this.options = {
692
+ defaultPreloadDelay: 50,
693
+ context: undefined,
694
+ ...options,
695
+ stringifySearch: options?.stringifySearch ?? defaultStringifySearch,
696
+ parseSearch: options?.parseSearch ?? defaultParseSearch
697
+ // fetchServerDataFn: options?.fetchServerDataFn ?? defaultFetchServerDataFn,
698
+ };
1767
699
 
1768
- if (!isServer && window.removeEventListener) {
1769
- // Be sure to unsubscribe if a new handler is set
1770
- window.removeEventListener('visibilitychange', router.onFocus);
1771
- window.removeEventListener('focus', router.onFocus);
700
+ this.__store = new Store(getInitialRouterState(), {
701
+ onUpdate: () => {
702
+ const prev = this.state;
703
+ this.state = this.__store.state;
704
+ const matchesByIdChanged = prev.matchesById !== this.state.matchesById;
705
+ let matchesChanged;
706
+ let pendingMatchesChanged;
707
+ if (!matchesByIdChanged) {
708
+ matchesChanged = prev.matchIds.length !== this.state.matchIds.length || prev.matchIds.some((d, i) => d !== this.state.matchIds[i]);
709
+ pendingMatchesChanged = prev.pendingMatchIds.length !== this.state.pendingMatchIds.length || prev.pendingMatchIds.some((d, i) => d !== this.state.pendingMatchIds[i]);
1772
710
  }
1773
- };
1774
- },
1775
- onFocus: () => {
1776
- router.load();
1777
- },
1778
- update: opts => {
1779
- const newHistory = (opts == null ? void 0 : opts.history) !== router.history;
1780
-
1781
- if (!router.location || newHistory) {
1782
- if (opts != null && opts.history) {
1783
- router.history = opts.history;
711
+ if (matchesByIdChanged || matchesChanged) {
712
+ this.state.matches = this.state.matchIds.map(id => {
713
+ return this.state.matchesById[id];
714
+ });
1784
715
  }
1785
-
1786
- router.location = router.__.parseLocation(router.history.location);
1787
- router.state.location = router.location;
1788
- }
1789
-
1790
- Object.assign(router.options, opts);
1791
- const {
1792
- basepath,
1793
- routeConfig
1794
- } = router.options;
1795
- router.basepath = cleanPath("/" + (basepath != null ? basepath : ''));
1796
-
1797
- if (routeConfig) {
1798
- router.routesById = {};
1799
- router.routeTree = router.__.buildRouteTree(routeConfig);
1800
- }
1801
-
1802
- return router;
1803
- },
1804
- cancelMatches: () => {
1805
- var _router$state$pending, _router$state$pending2;
1806
- [...router.state.matches, ...((_router$state$pending = (_router$state$pending2 = router.state.pending) == null ? void 0 : _router$state$pending2.matches) != null ? _router$state$pending : [])].forEach(match => {
1807
- match.cancel();
1808
- });
1809
- },
1810
- load: async next => {
1811
- const id = Math.random();
1812
- router.startedLoadingAt = id;
1813
-
1814
- if (next) {
1815
- // Ingest the new location
1816
- router.location = next;
1817
- } // Cancel any pending matches
1818
-
1819
-
1820
- router.cancelMatches(); // Match the routes
1821
-
1822
- const matches = router.matchRoutes(router.location.pathname, {
1823
- strictParseParams: true
716
+ if (matchesByIdChanged || pendingMatchesChanged) {
717
+ this.state.pendingMatches = this.state.pendingMatchIds.map(id => {
718
+ return this.state.matchesById[id];
719
+ });
720
+ }
721
+ this.state.isFetching = [...this.state.matches, ...this.state.pendingMatches].some(d => d.isFetching);
722
+ },
723
+ defaultPriority: 'low'
724
+ });
725
+ this.state = this.__store.state;
726
+ this.update(options);
727
+ const next = this.buildNext({
728
+ hash: true,
729
+ fromCurrent: true,
730
+ search: true,
731
+ state: true
732
+ });
733
+ if (this.state.location.href !== next.href) {
734
+ this.#commitLocation({
735
+ ...next,
736
+ replace: true
1824
737
  });
738
+ }
739
+ }
740
+ reset = () => {
741
+ this.__store.setState(s => Object.assign(s, getInitialRouterState()));
742
+ };
743
+ mount = () => {
744
+ // If the router matches are empty, start loading the matches
745
+ // if (!this.state.matches.length) {
746
+ this.safeLoad();
747
+ // }
748
+ };
1825
749
 
1826
- if (typeof document !== 'undefined') {
1827
- router.state = _extends({}, router.state, {
1828
- pending: {
1829
- matches: matches,
1830
- location: router.location
1831
- },
1832
- status: 'loading'
1833
- });
1834
- } else {
1835
- router.state = _extends({}, router.state, {
1836
- matches: matches,
1837
- location: router.location,
1838
- status: 'loading'
1839
- });
750
+ update = opts => {
751
+ this.options = {
752
+ ...this.options,
753
+ ...opts,
754
+ context: {
755
+ ...this.options.context,
756
+ ...opts?.context
1840
757
  }
1841
-
1842
- router.notify(); // Load the matches
1843
-
1844
- await router.loadMatches(matches);
1845
-
1846
- if (router.startedLoadingAt !== id) {
1847
- // Ignore side-effects of match loading
1848
- return router.navigationPromise;
758
+ };
759
+ if (!this.history || this.options.history && this.options.history !== this.history) {
760
+ if (this.#unsubHistory) {
761
+ this.#unsubHistory();
1849
762
  }
1850
-
1851
- const previousMatches = router.state.matches;
1852
- const exiting = [],
1853
- staying = [];
1854
- previousMatches.forEach(d => {
1855
- if (matches.find(dd => dd.matchId === d.matchId)) {
1856
- staying.push(d);
1857
- } else {
1858
- exiting.push(d);
1859
- }
1860
- });
1861
- const entering = matches.filter(d => {
1862
- return !previousMatches.find(dd => dd.matchId === d.matchId);
763
+ this.history = this.options.history ?? (isServer ? createMemoryHistory() : createBrowserHistory());
764
+ const parsedLocation = this.#parseLocation();
765
+ this.__store.setState(s => ({
766
+ ...s,
767
+ resolvedLocation: parsedLocation,
768
+ location: parsedLocation
769
+ }));
770
+ this.#unsubHistory = this.history.listen(() => {
771
+ this.safeLoad({
772
+ next: this.#parseLocation(this.state.location)
773
+ });
1863
774
  });
1864
- const now = Date.now();
1865
- exiting.forEach(d => {
1866
- var _ref, _d$options$loaderGcMa, _ref2, _d$options$loaderMaxA;
1867
-
1868
- d.__.onExit == null ? void 0 : d.__.onExit({
1869
- params: d.params,
1870
- search: d.routeSearch
1871
- }); // Clear idle error states when match leaves
1872
-
1873
- if (d.status === 'error' && !d.isFetching) {
1874
- d.status = 'idle';
1875
- d.error = undefined;
1876
- }
775
+ }
776
+ const {
777
+ basepath,
778
+ routeTree
779
+ } = this.options;
780
+ this.basepath = `/${trimPath(basepath ?? '') ?? ''}`;
781
+ if (routeTree && routeTree !== this.routeTree) {
782
+ this.#buildRouteTree(routeTree);
783
+ }
784
+ return this;
785
+ };
786
+ buildNext = opts => {
787
+ const next = this.#buildLocation(opts);
788
+ const __matches = this.matchRoutes(next.pathname, next.search);
789
+ return this.#buildLocation({
790
+ ...opts,
791
+ __matches
792
+ });
793
+ };
794
+ cancelMatches = () => {
795
+ this.state.matches.forEach(match => {
796
+ this.cancelMatch(match.id);
797
+ });
798
+ };
799
+ cancelMatch = id => {
800
+ this.getRouteMatch(id)?.abortController?.abort();
801
+ };
802
+ safeLoad = opts => {
803
+ return this.load(opts).catch(err => {
804
+ // console.warn(err)
805
+ // invariant(false, 'Encountered an error during router.load()! ☝️.')
806
+ });
807
+ };
808
+ latestLoadPromise = Promise.resolve();
809
+ load = async opts => {
810
+ const promise = new Promise(async (resolve, reject) => {
811
+ let latestPromise;
812
+ const checkLatest = () => {
813
+ return this.latestLoadPromise !== promise ? this.latestLoadPromise : undefined;
814
+ };
1877
815
 
1878
- const gc = Math.max((_ref = (_d$options$loaderGcMa = d.options.loaderGcMaxAge) != null ? _d$options$loaderGcMa : router.options.defaultLoaderGcMaxAge) != null ? _ref : 0, (_ref2 = (_d$options$loaderMaxA = d.options.loaderMaxAge) != null ? _d$options$loaderMaxA : router.options.defaultLoaderMaxAge) != null ? _ref2 : 0);
816
+ // Cancel any pending matches
817
+ // this.cancelMatches()
1879
818
 
1880
- if (gc > 0) {
1881
- router.matchCache[d.matchId] = {
1882
- gc: gc == Infinity ? Number.MAX_SAFE_INTEGER : now + gc,
1883
- match: d
1884
- };
819
+ let pendingMatches;
820
+ this.__store.batch(() => {
821
+ if (opts?.next) {
822
+ // Ingest the new location
823
+ this.__store.setState(s => ({
824
+ ...s,
825
+ location: opts.next
826
+ }));
1885
827
  }
1886
- });
1887
- staying.forEach(d => {
1888
- d.options.onTransition == null ? void 0 : d.options.onTransition({
1889
- params: d.params,
1890
- search: d.routeSearch
1891
- });
1892
- });
1893
- entering.forEach(d => {
1894
- d.__.onExit = d.options.onMatch == null ? void 0 : d.options.onMatch({
1895
- params: d.params,
1896
- search: d.search
828
+
829
+ // Match the routes
830
+ pendingMatches = this.matchRoutes(this.state.location.pathname, this.state.location.search, {
831
+ throwOnError: opts?.throwOnError,
832
+ debug: true
1897
833
  });
1898
- delete router.matchCache[d.matchId];
834
+ this.__store.setState(s => ({
835
+ ...s,
836
+ status: 'pending',
837
+ pendingMatchIds: pendingMatches.map(d => d.id),
838
+ matchesById: this.#mergeMatches(s.matchesById, pendingMatches)
839
+ }));
1899
840
  });
841
+ try {
842
+ // Load the matches
843
+ await this.loadMatches(pendingMatches);
1900
844
 
1901
- if (router.startedLoadingAt !== id) {
1902
- // Ignore side-effects of match loading
1903
- return;
1904
- }
1905
-
1906
- matches.forEach(match => {
1907
- // Clear actions
1908
- if (match.action) {
1909
- match.action.current = undefined;
1910
- match.action.submissions = [];
845
+ // Only apply the latest transition
846
+ if (latestPromise = checkLatest()) {
847
+ return await latestPromise;
1911
848
  }
1912
- });
1913
- router.state = _extends({}, router.state, {
1914
- location: router.location,
1915
- matches,
1916
- pending: undefined,
1917
- status: 'idle'
1918
- });
1919
- router.notify();
1920
- router.resolveNavigation();
1921
- },
1922
- cleanMatchCache: () => {
1923
- const now = Date.now();
1924
- Object.keys(router.matchCache).forEach(matchId => {
1925
- const entry = router.matchCache[matchId]; // Don't remove loading matches
1926
-
1927
- if (entry.match.status === 'loading') {
1928
- return;
1929
- } // Do not remove successful matches that are still valid
1930
-
1931
-
1932
- if (entry.gc > 0 && entry.gc > now) {
1933
- return;
1934
- } // Everything else gets removed
1935
-
1936
-
1937
- delete router.matchCache[matchId];
1938
- });
1939
- },
1940
- loadRoute: async function loadRoute(navigateOpts) {
1941
- if (navigateOpts === void 0) {
1942
- navigateOpts = router.location;
849
+ const prevLocation = this.state.resolvedLocation;
850
+ this.__store.setState(s => ({
851
+ ...s,
852
+ status: 'idle',
853
+ resolvedLocation: s.location,
854
+ matchIds: s.pendingMatchIds,
855
+ pendingMatchIds: []
856
+ }));
857
+ if (prevLocation.href !== this.state.location.href) {
858
+ this.options.onRouteChange?.();
859
+ }
860
+ resolve();
861
+ } catch (err) {
862
+ // Only apply the latest transition
863
+ if (latestPromise = checkLatest()) {
864
+ return await latestPromise;
865
+ }
866
+ reject(err);
1943
867
  }
1944
-
1945
- const next = router.buildNext(navigateOpts);
1946
- const matches = router.matchRoutes(next.pathname, {
1947
- strictParseParams: true
1948
- });
1949
- await router.loadMatches(matches);
1950
- return matches;
1951
- },
1952
- preloadRoute: async function preloadRoute(navigateOpts, loaderOpts) {
1953
- var _ref3, _ref4, _loaderOpts$maxAge, _ref5, _ref6, _loaderOpts$gcMaxAge;
1954
-
1955
- if (navigateOpts === void 0) {
1956
- navigateOpts = router.location;
868
+ });
869
+ this.latestLoadPromise = promise;
870
+ return this.latestLoadPromise;
871
+ };
872
+ #mergeMatches = (prevMatchesById, nextMatches) => {
873
+ const nextMatchesById = {
874
+ ...prevMatchesById
875
+ };
876
+ let hadNew = false;
877
+ nextMatches.forEach(match => {
878
+ if (!nextMatchesById[match.id]) {
879
+ hadNew = true;
880
+ nextMatchesById[match.id] = match;
1957
881
  }
1958
-
1959
- const next = router.buildNext(navigateOpts);
1960
- const matches = router.matchRoutes(next.pathname, {
1961
- strictParseParams: true
882
+ });
883
+ if (!hadNew) {
884
+ return prevMatchesById;
885
+ }
886
+ return nextMatchesById;
887
+ };
888
+ getRoute = id => {
889
+ const route = this.routesById[id];
890
+ invariant(route, `Route with id "${id}" not found`);
891
+ return route;
892
+ };
893
+ preloadRoute = async (navigateOpts = this.state.location) => {
894
+ const next = this.buildNext(navigateOpts);
895
+ const matches = this.matchRoutes(next.pathname, next.search, {
896
+ throwOnError: true
897
+ });
898
+ this.__store.setState(s => {
899
+ return {
900
+ ...s,
901
+ matchesById: this.#mergeMatches(s.matchesById, matches)
902
+ };
903
+ });
904
+ await this.loadMatches(matches, {
905
+ preload: true,
906
+ maxAge: navigateOpts.maxAge
907
+ });
908
+ return matches;
909
+ };
910
+ cleanMatches = () => {
911
+ const now = Date.now();
912
+ const outdatedMatchIds = Object.values(this.state.matchesById).filter(match => {
913
+ const route = this.getRoute(match.routeId);
914
+ 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);
915
+ }).map(d => d.id);
916
+ if (outdatedMatchIds.length) {
917
+ this.__store.setState(s => {
918
+ const matchesById = {
919
+ ...s.matchesById
920
+ };
921
+ outdatedMatchIds.forEach(id => {
922
+ delete matchesById[id];
923
+ });
924
+ return {
925
+ ...s,
926
+ matchesById
927
+ };
1962
928
  });
1963
- await router.loadMatches(matches, {
1964
- preload: true,
1965
- maxAge: (_ref3 = (_ref4 = (_loaderOpts$maxAge = loaderOpts.maxAge) != null ? _loaderOpts$maxAge : router.options.defaultPreloadMaxAge) != null ? _ref4 : router.options.defaultLoaderMaxAge) != null ? _ref3 : 0,
1966
- gcMaxAge: (_ref5 = (_ref6 = (_loaderOpts$gcMaxAge = loaderOpts.gcMaxAge) != null ? _loaderOpts$gcMaxAge : router.options.defaultPreloadGcMaxAge) != null ? _ref6 : router.options.defaultLoaderGcMaxAge) != null ? _ref5 : 0
929
+ }
930
+ };
931
+ matchRoutes = (pathname, locationSearch, opts) => {
932
+ let routeParams = {};
933
+ let foundRoute = this.flatRoutes.find(route => {
934
+ const matchedParams = matchPathname(this.basepath, pathname, {
935
+ to: route.fullPath,
936
+ caseSensitive: route.options.caseSensitive ?? this.options.caseSensitive
1967
937
  });
1968
- return matches;
1969
- },
1970
- matchRoutes: (pathname, opts) => {
1971
- var _router$state$pending3, _router$state$pending4;
1972
-
1973
- router.cleanMatchCache();
1974
- const matches = [];
1975
-
1976
- if (!router.routeTree) {
1977
- return matches;
938
+ if (matchedParams) {
939
+ routeParams = matchedParams;
940
+ return true;
1978
941
  }
942
+ return false;
943
+ });
944
+ let routeCursor = foundRoute || this.routesById['__root__'];
945
+ let matchedRoutes = [routeCursor];
946
+ while (routeCursor?.parentRoute) {
947
+ routeCursor = routeCursor.parentRoute;
948
+ if (routeCursor) matchedRoutes.unshift(routeCursor);
949
+ }
1979
950
 
1980
- const existingMatches = [...router.state.matches, ...((_router$state$pending3 = (_router$state$pending4 = router.state.pending) == null ? void 0 : _router$state$pending4.matches) != null ? _router$state$pending3 : [])];
1981
-
1982
- const recurse = async routes => {
1983
- var _parentMatch$params, _router$options$filte, _foundRoute$childRout;
1984
-
1985
- const parentMatch = last(matches);
1986
- let params = (_parentMatch$params = parentMatch == null ? void 0 : parentMatch.params) != null ? _parentMatch$params : {};
1987
- const filteredRoutes = (_router$options$filte = router.options.filterRoutes == null ? void 0 : router.options.filterRoutes(routes)) != null ? _router$options$filte : routes;
1988
- let foundRoutes = [];
1989
-
1990
- const findMatchInRoutes = (parentRoutes, routes) => {
1991
- routes.some(route => {
1992
- var _route$childRoutes, _route$childRoutes2, _route$options$caseSe;
1993
-
1994
- if (!route.routePath && (_route$childRoutes = route.childRoutes) != null && _route$childRoutes.length) {
1995
- return findMatchInRoutes([...foundRoutes, route], route.childRoutes);
1996
- }
1997
-
1998
- const fuzzy = !!(route.routePath !== '/' || (_route$childRoutes2 = route.childRoutes) != null && _route$childRoutes2.length);
1999
- const matchParams = matchPathname(pathname, {
2000
- to: route.fullPath,
2001
- fuzzy,
2002
- caseSensitive: (_route$options$caseSe = route.options.caseSensitive) != null ? _route$options$caseSe : router.options.caseSensitive
2003
- });
2004
-
2005
- if (matchParams) {
2006
- let parsedParams;
2007
-
2008
- try {
2009
- var _route$options$parseP;
2010
-
2011
- parsedParams = (_route$options$parseP = route.options.parseParams == null ? void 0 : route.options.parseParams(matchParams)) != null ? _route$options$parseP : matchParams;
2012
- } catch (err) {
2013
- if (opts != null && opts.strictParseParams) {
2014
- throw err;
2015
- }
2016
- }
2017
-
2018
- params = _extends({}, params, parsedParams);
2019
- }
2020
-
2021
- if (!!matchParams) {
2022
- foundRoutes = [...parentRoutes, route];
2023
- }
2024
-
2025
- return !!foundRoutes.length;
2026
- });
2027
- return !!foundRoutes.length;
2028
- };
2029
-
2030
- findMatchInRoutes([], filteredRoutes);
2031
-
2032
- if (!foundRoutes.length) {
2033
- return;
2034
- }
951
+ // Alright, by now we should have all of our
952
+ // matching routes and their param pairs, let's
953
+ // Turn them into actual `Match` objects and
954
+ // accumulate the params into a single params bag
955
+ let allParams = {};
2035
956
 
2036
- foundRoutes.forEach(foundRoute => {
2037
- var _router$matchCache$ma;
957
+ // Existing matches are matches that are already loaded along with
958
+ // pending matches that are still loading
2038
959
 
2039
- const interpolatedPath = interpolatePath(foundRoute.routePath, params);
2040
- const matchId = interpolatePath(foundRoute.routeId, params, true);
2041
- const match = existingMatches.find(d => d.matchId === matchId) || ((_router$matchCache$ma = router.matchCache[matchId]) == null ? void 0 : _router$matchCache$ma.match) || createRouteMatch(router, foundRoute, {
2042
- matchId,
2043
- params,
2044
- pathname: joinPaths([pathname, interpolatedPath])
2045
- });
2046
- matches.push(match);
960
+ const matches = matchedRoutes.map(route => {
961
+ let parsedParams;
962
+ let parsedParamsError;
963
+ try {
964
+ parsedParams = route.options.parseParams?.(routeParams) ?? routeParams;
965
+ // (typeof route.options.parseParams === 'object' &&
966
+ // route.options.parseParams.parse
967
+ // ? route.options.parseParams.parse(routeParams)
968
+ // : (route.options.parseParams as any)?.(routeParams!)) ?? routeParams
969
+ } catch (err) {
970
+ parsedParamsError = new PathParamError(err.message, {
971
+ cause: err
2047
972
  });
2048
- const foundRoute = last(foundRoutes);
2049
-
2050
- if ((_foundRoute$childRout = foundRoute.childRoutes) != null && _foundRoute$childRout.length) {
2051
- recurse(foundRoute.childRoutes);
973
+ if (opts?.throwOnError) {
974
+ throw parsedParamsError;
2052
975
  }
2053
- };
976
+ }
2054
977
 
2055
- recurse([router.routeTree]);
2056
- cascadeLoaderData(matches);
2057
- return matches;
2058
- },
2059
- loadMatches: async (resolvedMatches, loaderOpts) => {
2060
- const matchPromises = resolvedMatches.map(async match => {
2061
- // Validate the match (loads search params etc)
2062
- match.__.validate();
978
+ // Add the parsed params to the accumulated params bag
979
+ Object.assign(allParams, parsedParams);
980
+ const interpolatedPath = interpolatePath(route.path, allParams);
981
+ const key = route.options.key ? route.options.key({
982
+ params: allParams,
983
+ search: locationSearch
984
+ }) ?? '' : '';
985
+ const stringifiedKey = key ? JSON.stringify(key) : '';
986
+ const matchId = interpolatePath(route.id, allParams, true) + stringifiedKey;
987
+
988
+ // Waste not, want not. If we already have a match for this route,
989
+ // reuse it. This is important for layout routes, which might stick
990
+ // around between navigation actions that only change leaf routes.
991
+ const existingMatch = this.getRouteMatch(matchId);
992
+ if (existingMatch) {
993
+ return {
994
+ ...existingMatch
995
+ };
996
+ }
2063
997
 
2064
- match.load(loaderOpts);
998
+ // Create a fresh route match
999
+ const hasLoaders = !!(route.options.loader || componentTypes.some(d => route.options[d]?.preload));
1000
+ const routeMatch = {
1001
+ id: matchId,
1002
+ key: stringifiedKey,
1003
+ routeId: route.id,
1004
+ params: allParams,
1005
+ pathname: joinPaths([this.basepath, interpolatedPath]),
1006
+ updatedAt: Date.now(),
1007
+ invalidAt: Infinity,
1008
+ preloadInvalidAt: Infinity,
1009
+ routeSearch: {},
1010
+ search: {},
1011
+ status: hasLoaders ? 'idle' : 'success',
1012
+ isFetching: false,
1013
+ invalid: false,
1014
+ error: undefined,
1015
+ paramsError: parsedParamsError,
1016
+ searchError: undefined,
1017
+ loaderData: undefined,
1018
+ loadPromise: Promise.resolve(),
1019
+ routeContext: undefined,
1020
+ context: undefined,
1021
+ abortController: new AbortController(),
1022
+ fetchedAt: 0
1023
+ };
1024
+ return routeMatch;
1025
+ });
2065
1026
 
2066
- if (match.__.loadPromise) {
2067
- // Wait for the first sign of activity from the match
2068
- await match.__.loadPromise;
1027
+ // Take each match and resolve its search params and context
1028
+ // This has to happen after the matches are created or found
1029
+ // so that we can use the parent match's search params and context
1030
+ matches.forEach((match, i) => {
1031
+ const parentMatch = matches[i - 1];
1032
+ const route = this.getRoute(match.routeId);
1033
+ const searchInfo = (() => {
1034
+ // Validate the search params and stabilize them
1035
+ const parentSearchInfo = {
1036
+ search: parentMatch?.search ?? locationSearch,
1037
+ routeSearch: parentMatch?.routeSearch ?? locationSearch
1038
+ };
1039
+ try {
1040
+ const validator = typeof route.options.validateSearch === 'object' ? route.options.validateSearch.parse : route.options.validateSearch;
1041
+ const routeSearch = validator?.(parentSearchInfo.search) ?? {};
1042
+ const search = {
1043
+ ...parentSearchInfo.search,
1044
+ ...routeSearch
1045
+ };
1046
+ return {
1047
+ routeSearch: replaceEqualDeep(match.routeSearch, routeSearch),
1048
+ search: replaceEqualDeep(match.search, search)
1049
+ };
1050
+ } catch (err) {
1051
+ match.searchError = new SearchParamError(err.message, {
1052
+ cause: err
1053
+ });
1054
+ if (opts?.throwOnError) {
1055
+ throw match.searchError;
1056
+ }
1057
+ return parentSearchInfo;
2069
1058
  }
2070
- });
2071
- router.notify();
2072
- await Promise.all(matchPromises);
2073
- },
2074
- invalidateRoute: opts => {
2075
- var _router$state$pending5, _router$state$pending6;
2076
-
2077
- const next = router.buildNext(opts);
2078
- const unloadedMatchIds = router.matchRoutes(next.pathname).map(d => d.matchId);
2079
- [...router.state.matches, ...((_router$state$pending5 = (_router$state$pending6 = router.state.pending) == null ? void 0 : _router$state$pending6.matches) != null ? _router$state$pending5 : [])].forEach(match => {
2080
- if (unloadedMatchIds.includes(match.matchId)) {
2081
- match.invalidate();
1059
+ })();
1060
+ const contextInfo = (() => {
1061
+ try {
1062
+ const routeContext = route.options.getContext?.({
1063
+ parentContext: parentMatch?.routeContext ?? {},
1064
+ context: parentMatch?.context ?? this?.options.context ?? {},
1065
+ params: match.params,
1066
+ search: match.search
1067
+ }) || {};
1068
+ const context = {
1069
+ ...(parentMatch?.context ?? this?.options.context),
1070
+ ...routeContext
1071
+ };
1072
+ return {
1073
+ context,
1074
+ routeContext
1075
+ };
1076
+ } catch (err) {
1077
+ route.options.onError?.(err);
1078
+ throw err;
2082
1079
  }
1080
+ })();
1081
+ Object.assign(match, {
1082
+ ...searchInfo,
1083
+ ...contextInfo
2083
1084
  });
2084
- },
2085
- reload: () => router.__.navigate({
2086
- fromCurrent: true,
2087
- replace: true,
2088
- search: true
2089
- }),
2090
- resolvePath: (from, path) => {
2091
- return resolvePath(router.basepath, from, cleanPath(path));
2092
- },
2093
- matchRoute: (location, opts) => {
2094
- var _location$from;
2095
-
2096
- // const location = router.buildNext(opts)
2097
- location = _extends({}, location, {
2098
- to: location.to ? router.resolvePath((_location$from = location.from) != null ? _location$from : '', location.to) : undefined
2099
- });
2100
- const next = router.buildNext(location);
2101
-
2102
- if (opts != null && opts.pending) {
2103
- var _router$state$pending7;
1085
+ });
1086
+ return matches;
1087
+ };
1088
+ loadMatches = async (resolvedMatches, opts) => {
1089
+ this.cleanMatches();
1090
+ let firstBadMatchIndex;
2104
1091
 
2105
- if (!((_router$state$pending7 = router.state.pending) != null && _router$state$pending7.location)) {
2106
- return false;
1092
+ // Check each match middleware to see if the route can be accessed
1093
+ try {
1094
+ await Promise.all(resolvedMatches.map(async (match, index) => {
1095
+ const route = this.getRoute(match.routeId);
1096
+ if (!opts?.preload) {
1097
+ // Update each match with its latest url data
1098
+ this.setRouteMatch(match.id, s => ({
1099
+ ...s,
1100
+ routeSearch: match.routeSearch,
1101
+ search: match.search,
1102
+ routeContext: match.routeContext,
1103
+ context: match.context,
1104
+ error: match.error,
1105
+ paramsError: match.paramsError,
1106
+ searchError: match.searchError,
1107
+ params: match.params
1108
+ }));
2107
1109
  }
2108
-
2109
- return !!matchPathname(router.state.pending.location.pathname, _extends({}, opts, {
2110
- to: next.pathname
2111
- }));
2112
- }
2113
-
2114
- return !!matchPathname(router.state.location.pathname, _extends({}, opts, {
2115
- to: next.pathname
2116
- }));
2117
- },
2118
- navigate: async _ref7 => {
2119
- let {
2120
- from,
2121
- to = '.',
2122
- search,
2123
- hash,
2124
- replace,
2125
- params
2126
- } = _ref7;
2127
- // If this link simply reloads the current route,
2128
- // make sure it has a new key so it will trigger a data refresh
2129
- // If this `to` is a valid external URL, return
2130
- // null for LinkUtils
2131
- const toString = String(to);
2132
- const fromString = String(from);
2133
- let isExternal;
2134
-
2135
- try {
2136
- new URL("" + toString);
2137
- isExternal = true;
2138
- } catch (e) {}
2139
-
2140
- invariant(!isExternal, 'Attempting to navigate to external url with router.navigate!');
2141
- return router.__.navigate({
2142
- from: fromString,
2143
- to: toString,
2144
- search,
2145
- hash,
2146
- replace,
2147
- params
2148
- });
2149
- },
2150
- buildLink: _ref8 => {
2151
- var _preload, _ref9;
2152
-
2153
- let {
2154
- from,
2155
- to = '.',
2156
- search,
2157
- params,
2158
- hash,
2159
- target,
2160
- replace,
2161
- activeOptions,
2162
- preload,
2163
- preloadMaxAge: userPreloadMaxAge,
2164
- preloadGcMaxAge: userPreloadGcMaxAge,
2165
- preloadDelay: userPreloadDelay,
2166
- disabled
2167
- } = _ref8;
2168
-
2169
- // If this link simply reloads the current route,
2170
- // make sure it has a new key so it will trigger a data refresh
2171
- // If this `to` is a valid external URL, return
2172
- // null for LinkUtils
2173
- try {
2174
- new URL("" + to);
2175
- return {
2176
- type: 'external',
2177
- href: to
1110
+ const handleError = (err, handler) => {
1111
+ firstBadMatchIndex = firstBadMatchIndex ?? index;
1112
+ handler = handler || route.options.onError;
1113
+ if (isRedirect(err)) {
1114
+ throw err;
1115
+ }
1116
+ try {
1117
+ handler?.(err);
1118
+ } catch (errorHandlerErr) {
1119
+ err = errorHandlerErr;
1120
+ if (isRedirect(errorHandlerErr)) {
1121
+ throw errorHandlerErr;
1122
+ }
1123
+ }
1124
+ this.setRouteMatch(match.id, s => ({
1125
+ ...s,
1126
+ error: err,
1127
+ status: 'error',
1128
+ updatedAt: Date.now()
1129
+ }));
2178
1130
  };
2179
- } catch (e) {}
2180
-
2181
- const nextOpts = {
2182
- from,
2183
- to,
2184
- search,
2185
- params,
2186
- hash,
2187
- replace
2188
- };
2189
- const next = router.buildNext(nextOpts);
2190
- preload = (_preload = preload) != null ? _preload : router.options.defaultPreload;
2191
- const preloadDelay = (_ref9 = userPreloadDelay != null ? userPreloadDelay : router.options.defaultPreloadDelay) != null ? _ref9 : 0; // Compare path/hash for matches
2192
-
2193
- const pathIsEqual = router.state.location.pathname === next.pathname;
2194
- const currentPathSplit = router.state.location.pathname.split('/');
2195
- const nextPathSplit = next.pathname.split('/');
2196
- const pathIsFuzzyEqual = nextPathSplit.every((d, i) => d === currentPathSplit[i]);
2197
- const hashIsEqual = router.state.location.hash === next.hash; // Combine the matches based on user options
2198
-
2199
- const pathTest = activeOptions != null && activeOptions.exact ? pathIsEqual : pathIsFuzzyEqual;
2200
- const hashTest = activeOptions != null && activeOptions.includeHash ? hashIsEqual : true; // The final "active" test
2201
-
2202
- const isActive = pathTest && hashTest; // The click handler
2203
-
2204
- const handleClick = e => {
2205
- if (!disabled && !isCtrlEvent(e) && !e.defaultPrevented && (!target || target === '_self') && e.button === 0) {
2206
- e.preventDefault();
2207
-
2208
- if (pathIsEqual && !search && !hash) {
2209
- router.invalidateRoute(nextOpts);
2210
- } // All is well? Navigate!)
2211
-
2212
-
2213
- router.__.navigate(nextOpts);
1131
+ if (match.paramsError) {
1132
+ handleError(match.paramsError, route.options.onParseParamsError);
2214
1133
  }
2215
- }; // The click handler
2216
-
2217
-
2218
- const handleFocus = e => {
2219
- if (preload) {
2220
- router.preloadRoute(nextOpts, {
2221
- maxAge: userPreloadMaxAge,
2222
- gcMaxAge: userPreloadGcMaxAge
1134
+ if (match.searchError) {
1135
+ handleError(match.searchError, route.options.onValidateSearchError);
1136
+ }
1137
+ try {
1138
+ await route.options.beforeLoad?.({
1139
+ ...match,
1140
+ preload: !!opts?.preload
2223
1141
  });
1142
+ } catch (err) {
1143
+ handleError(err, route.options.onBeforeLoadError);
2224
1144
  }
2225
- };
2226
-
2227
- const handleEnter = e => {
2228
- const target = e.target || {};
2229
-
2230
- if (preload) {
2231
- if (target.preloadTimeout) {
2232
- return;
1145
+ }));
1146
+ } catch (err) {
1147
+ if (!opts?.preload) {
1148
+ this.navigate(err);
1149
+ }
1150
+ throw err;
1151
+ }
1152
+ const validResolvedMatches = resolvedMatches.slice(0, firstBadMatchIndex);
1153
+ const matchPromises = [];
1154
+ validResolvedMatches.forEach((match, index) => {
1155
+ matchPromises.push((async () => {
1156
+ const parentMatchPromise = matchPromises[index - 1];
1157
+ const route = this.getRoute(match.routeId);
1158
+ if (match.isFetching || match.status === 'success' && !this.getIsInvalid({
1159
+ matchId: match.id,
1160
+ preload: opts?.preload
1161
+ })) {
1162
+ return this.getRouteMatch(match.id)?.loadPromise;
1163
+ }
1164
+ const fetchedAt = Date.now();
1165
+ const checkLatest = () => {
1166
+ const latest = this.getRouteMatch(match.id);
1167
+ return latest && latest.fetchedAt !== fetchedAt ? latest.loadPromise : undefined;
1168
+ };
1169
+ const loadPromise = (async () => {
1170
+ let latestPromise;
1171
+ const componentsPromise = Promise.all(componentTypes.map(async type => {
1172
+ const component = route.options[type];
1173
+ if (component?.preload) {
1174
+ await component.preload();
1175
+ }
1176
+ }));
1177
+ const loaderPromise = route.options.loader?.({
1178
+ ...match,
1179
+ preload: !!opts?.preload,
1180
+ parentMatchPromise
1181
+ });
1182
+ const handleError = err => {
1183
+ if (isRedirect(err)) {
1184
+ if (!opts?.preload) {
1185
+ this.navigate(err);
1186
+ }
1187
+ return true;
1188
+ }
1189
+ return false;
1190
+ };
1191
+ try {
1192
+ const [_, loader] = await Promise.all([componentsPromise, loaderPromise]);
1193
+ if (latestPromise = checkLatest()) return await latestPromise;
1194
+ this.setRouteMatchData(match.id, () => loader, opts);
1195
+ } catch (err) {
1196
+ if (latestPromise = checkLatest()) return await latestPromise;
1197
+ if (handleError(err)) {
1198
+ return;
1199
+ }
1200
+ const errorHandler = route.options.onLoadError ?? route.options.onError;
1201
+ let caughtError = err;
1202
+ try {
1203
+ errorHandler?.(err);
1204
+ } catch (errorHandlerErr) {
1205
+ caughtError = errorHandlerErr;
1206
+ if (handleError(errorHandlerErr)) {
1207
+ return;
1208
+ }
1209
+ }
1210
+ this.setRouteMatch(match.id, s => ({
1211
+ ...s,
1212
+ error: caughtError,
1213
+ status: 'error',
1214
+ isFetching: false,
1215
+ updatedAt: Date.now()
1216
+ }));
2233
1217
  }
1218
+ })();
1219
+ this.setRouteMatch(match.id, s => ({
1220
+ ...s,
1221
+ status: s.status !== 'success' ? 'pending' : s.status,
1222
+ isFetching: true,
1223
+ loadPromise,
1224
+ fetchedAt,
1225
+ invalid: false
1226
+ }));
1227
+ await loadPromise;
1228
+ })());
1229
+ });
1230
+ await Promise.all(matchPromises);
1231
+ };
1232
+ reload = () => {
1233
+ return this.navigate({
1234
+ fromCurrent: true,
1235
+ replace: true,
1236
+ search: true
1237
+ });
1238
+ };
1239
+ resolvePath = (from, path) => {
1240
+ return resolvePath(this.basepath, from, cleanPath(path));
1241
+ };
1242
+ navigate = async ({
1243
+ from,
1244
+ to = '',
1245
+ search,
1246
+ hash,
1247
+ replace,
1248
+ params
1249
+ }) => {
1250
+ // If this link simply reloads the current route,
1251
+ // make sure it has a new key so it will trigger a data refresh
1252
+
1253
+ // If this `to` is a valid external URL, return
1254
+ // null for LinkUtils
1255
+ const toString = String(to);
1256
+ const fromString = typeof from === 'undefined' ? from : String(from);
1257
+ let isExternal;
1258
+ try {
1259
+ new URL(`${toString}`);
1260
+ isExternal = true;
1261
+ } catch (e) {}
1262
+ invariant(!isExternal, 'Attempting to navigate to external url with this.navigate!');
1263
+ return this.#commitLocation({
1264
+ from: fromString,
1265
+ to: toString,
1266
+ search,
1267
+ hash,
1268
+ replace,
1269
+ params
1270
+ });
1271
+ };
1272
+ matchRoute = (location, opts) => {
1273
+ location = {
1274
+ ...location,
1275
+ to: location.to ? this.resolvePath(location.from ?? '', location.to) : undefined
1276
+ };
1277
+ const next = this.buildNext(location);
1278
+ if (opts?.pending && this.state.status !== 'pending') {
1279
+ return false;
1280
+ }
1281
+ const baseLocation = opts?.pending ? this.state.location : this.state.resolvedLocation;
1282
+ if (!baseLocation) {
1283
+ return false;
1284
+ }
1285
+ const match = matchPathname(this.basepath, baseLocation.pathname, {
1286
+ ...opts,
1287
+ to: next.pathname
1288
+ });
1289
+ if (!match) {
1290
+ return false;
1291
+ }
1292
+ if (opts?.includeSearch ?? true) {
1293
+ return partialDeepEqual(baseLocation.search, next.search) ? match : false;
1294
+ }
1295
+ return match;
1296
+ };
1297
+ buildLink = ({
1298
+ from,
1299
+ to = '.',
1300
+ search,
1301
+ params,
1302
+ hash,
1303
+ target,
1304
+ replace,
1305
+ activeOptions,
1306
+ preload,
1307
+ preloadDelay: userPreloadDelay,
1308
+ disabled
1309
+ }) => {
1310
+ // If this link simply reloads the current route,
1311
+ // make sure it has a new key so it will trigger a data refresh
1312
+
1313
+ // If this `to` is a valid external URL, return
1314
+ // null for LinkUtils
2234
1315
 
2235
- target.preloadTimeout = setTimeout(() => {
2236
- target.preloadTimeout = null;
2237
- router.preloadRoute(nextOpts, {
2238
- maxAge: userPreloadMaxAge,
2239
- gcMaxAge: userPreloadGcMaxAge
2240
- });
2241
- }, preloadDelay);
2242
- }
1316
+ try {
1317
+ new URL(`${to}`);
1318
+ return {
1319
+ type: 'external',
1320
+ href: to
2243
1321
  };
1322
+ } catch (e) {}
1323
+ const nextOpts = {
1324
+ from,
1325
+ to,
1326
+ search,
1327
+ params,
1328
+ hash,
1329
+ replace
1330
+ };
1331
+ const next = this.buildNext(nextOpts);
1332
+ preload = preload ?? this.options.defaultPreload;
1333
+ const preloadDelay = userPreloadDelay ?? this.options.defaultPreloadDelay ?? 0;
1334
+
1335
+ // Compare path/hash for matches
1336
+ const currentPathSplit = this.state.location.pathname.split('/');
1337
+ const nextPathSplit = next.pathname.split('/');
1338
+ const pathIsFuzzyEqual = nextPathSplit.every((d, i) => d === currentPathSplit[i]);
1339
+ // Combine the matches based on user options
1340
+ const pathTest = activeOptions?.exact ? this.state.location.pathname === next.pathname : pathIsFuzzyEqual;
1341
+ const hashTest = activeOptions?.includeHash ? this.state.location.hash === next.hash : true;
1342
+ const searchTest = activeOptions?.includeSearch ?? true ? partialDeepEqual(this.state.location.search, next.search) : true;
1343
+
1344
+ // The final "active" test
1345
+ const isActive = pathTest && hashTest && searchTest;
1346
+
1347
+ // The click handler
1348
+ const handleClick = e => {
1349
+ if (!disabled && !isCtrlEvent(e) && !e.defaultPrevented && (!target || target === '_self') && e.button === 0) {
1350
+ e.preventDefault();
1351
+
1352
+ // All is well? Navigate!
1353
+ this.#commitLocation(nextOpts);
1354
+ }
1355
+ };
2244
1356
 
2245
- const handleLeave = e => {
2246
- const target = e.target || {};
2247
-
1357
+ // The click handler
1358
+ const handleFocus = e => {
1359
+ if (preload) {
1360
+ this.preloadRoute(nextOpts).catch(err => {
1361
+ console.warn(err);
1362
+ console.warn('Error preloading route! ☝️');
1363
+ });
1364
+ }
1365
+ };
1366
+ const handleTouchStart = e => {
1367
+ this.preloadRoute(nextOpts).catch(err => {
1368
+ console.warn(err);
1369
+ console.warn('Error preloading route! ☝️');
1370
+ });
1371
+ };
1372
+ const handleEnter = e => {
1373
+ const target = e.target || {};
1374
+ if (preload) {
2248
1375
  if (target.preloadTimeout) {
2249
- clearTimeout(target.preloadTimeout);
2250
- target.preloadTimeout = null;
1376
+ return;
2251
1377
  }
2252
- };
2253
-
1378
+ target.preloadTimeout = setTimeout(() => {
1379
+ target.preloadTimeout = null;
1380
+ this.preloadRoute(nextOpts).catch(err => {
1381
+ console.warn(err);
1382
+ console.warn('Error preloading route! ☝️');
1383
+ });
1384
+ }, preloadDelay);
1385
+ }
1386
+ };
1387
+ const handleLeave = e => {
1388
+ const target = e.target || {};
1389
+ if (target.preloadTimeout) {
1390
+ clearTimeout(target.preloadTimeout);
1391
+ target.preloadTimeout = null;
1392
+ }
1393
+ };
1394
+ return {
1395
+ type: 'internal',
1396
+ next,
1397
+ handleFocus,
1398
+ handleClick,
1399
+ handleEnter,
1400
+ handleLeave,
1401
+ handleTouchStart,
1402
+ isActive,
1403
+ disabled
1404
+ };
1405
+ };
1406
+ dehydrate = () => {
1407
+ return {
1408
+ state: pick(this.state, ['location', 'status', 'lastUpdated'])
1409
+ };
1410
+ };
1411
+ hydrate = async __do_not_use_server_ctx => {
1412
+ let _ctx = __do_not_use_server_ctx;
1413
+ // Client hydrates from window
1414
+ if (typeof document !== 'undefined') {
1415
+ _ctx = window.__TSR_DEHYDRATED__;
1416
+ }
1417
+ invariant(_ctx, 'Expected to find a __TSR_DEHYDRATED__ property on window... but we did not. Did you forget to render <DehydrateRouter /> in your app?');
1418
+ const ctx = _ctx;
1419
+ this.dehydratedData = ctx.payload;
1420
+ this.options.hydrate?.(ctx.payload);
1421
+ this.__store.setState(s => {
2254
1422
  return {
2255
- type: 'internal',
2256
- next,
2257
- handleFocus,
2258
- handleClick,
2259
- handleEnter,
2260
- handleLeave,
2261
- isActive,
2262
- disabled
1423
+ ...s,
1424
+ ...ctx.router.state,
1425
+ resolvedLocation: ctx.router.state.location
2263
1426
  };
2264
- },
2265
- buildNext: opts => {
2266
- const next = router.__.buildLocation(opts);
2267
-
2268
- const matches = router.matchRoutes(next.pathname);
2269
-
2270
- const __preSearchFilters = matches.map(match => {
2271
- var _match$options$preSea;
2272
-
2273
- return (_match$options$preSea = match.options.preSearchFilters) != null ? _match$options$preSea : [];
2274
- }).flat().filter(Boolean);
2275
-
2276
- const __postSearchFilters = matches.map(match => {
2277
- var _match$options$postSe;
2278
-
2279
- return (_match$options$postSe = match.options.postSearchFilters) != null ? _match$options$postSe : [];
2280
- }).flat().filter(Boolean);
2281
-
2282
- return router.__.buildLocation(_extends({}, opts, {
2283
- __preSearchFilters,
2284
- __postSearchFilters
2285
- }));
2286
- },
2287
- __: {
2288
- buildRouteTree: rootRouteConfig => {
2289
- const recurseRoutes = (routeConfigs, parent) => {
2290
- return routeConfigs.map(routeConfig => {
2291
- const routeOptions = routeConfig.options;
2292
- const route = createRoute(routeConfig, routeOptions, parent, router);
2293
- const existingRoute = router.routesById[route.routeId];
2294
-
2295
- if (existingRoute) {
2296
- if (process.env.NODE_ENV !== 'production') {
2297
- console.warn("Duplicate routes found with id: " + String(route.routeId), router.routesById, route);
2298
- }
2299
-
2300
- throw new Error();
2301
- }
2302
- router.routesById[route.routeId] = route;
2303
- const children = routeConfig.children;
2304
- route.childRoutes = children != null && children.length ? recurseRoutes(children, route) : undefined;
2305
- return route;
2306
- });
2307
- };
2308
-
2309
- const routes = recurseRoutes([rootRouteConfig]);
2310
- return routes[0];
2311
- },
2312
- parseLocation: (location, previousLocation) => {
2313
- var _location$hash$split$;
2314
-
2315
- const parsedSearch = router.options.parseSearch(location.search);
2316
- return {
2317
- pathname: location.pathname,
2318
- searchStr: location.search,
2319
- search: replaceEqualDeep(previousLocation == null ? void 0 : previousLocation.search, parsedSearch),
2320
- hash: (_location$hash$split$ = location.hash.split('#').reverse()[0]) != null ? _location$hash$split$ : '',
2321
- href: "" + location.pathname + location.search + location.hash,
2322
- state: location.state,
2323
- key: location.key
2324
- };
2325
- },
2326
- navigate: location => {
2327
- const next = router.buildNext(location);
2328
- return router.__.commitLocation(next, location.replace);
2329
- },
2330
- buildLocation: function buildLocation(dest) {
2331
- var _dest$from, _router$basepath, _dest$to, _last, _dest$params, _dest$__preSearchFilt, _functionalUpdate, _dest$__preSearchFilt2, _dest$__postSearchFil;
2332
-
2333
- if (dest === void 0) {
2334
- dest = {};
2335
- }
2336
-
2337
- // const resolvedFrom: Location = {
2338
- // ...router.location,
2339
- const fromPathname = dest.fromCurrent ? router.location.pathname : (_dest$from = dest.from) != null ? _dest$from : router.location.pathname;
1427
+ });
1428
+ await this.load();
1429
+ return;
1430
+ };
1431
+ injectedHtml = [];
1432
+ injectHtml = async html => {
1433
+ this.injectedHtml.push(html);
1434
+ };
1435
+ dehydrateData = (key, getData) => {
1436
+ if (typeof document === 'undefined') {
1437
+ const strKey = typeof key === 'string' ? key : JSON.stringify(key);
1438
+ this.injectHtml(async () => {
1439
+ const id = `__TSR_DEHYDRATED__${strKey}`;
1440
+ const data = typeof getData === 'function' ? await getData() : getData;
1441
+ return `<script id='${id}' suppressHydrationWarning>window["__TSR_DEHYDRATED__${escapeJSON(strKey)}"] = ${JSON.stringify(data)}
1442
+ ;(() => {
1443
+ var el = document.getElementById('${id}')
1444
+ el.parentElement.removeChild(el)
1445
+ })()
1446
+ </script>`;
1447
+ });
1448
+ return () => this.hydrateData(key);
1449
+ }
1450
+ return () => undefined;
1451
+ };
1452
+ hydrateData = key => {
1453
+ if (typeof document !== 'undefined') {
1454
+ const strKey = typeof key === 'string' ? key : JSON.stringify(key);
1455
+ return window[`__TSR_DEHYDRATED__${strKey}`];
1456
+ }
1457
+ return undefined;
1458
+ };
2340
1459
 
2341
- let pathname = resolvePath((_router$basepath = router.basepath) != null ? _router$basepath : '/', fromPathname, "" + ((_dest$to = dest.to) != null ? _dest$to : '.'));
1460
+ // resolveMatchPromise = (matchId: string, key: string, value: any) => {
1461
+ // this.state.matches
1462
+ // .find((d) => d.id === matchId)
1463
+ // ?.__promisesByKey[key]?.resolve(value)
1464
+ // }
2342
1465
 
2343
- const fromMatches = router.matchRoutes(router.location.pathname, {
2344
- strictParseParams: true
1466
+ #buildRouteTree = routeTree => {
1467
+ this.routeTree = routeTree;
1468
+ this.routesById = {};
1469
+ this.routesByPath = {};
1470
+ this.flatRoutes = [];
1471
+ const recurseRoutes = routes => {
1472
+ routes.forEach((route, i) => {
1473
+ route.init({
1474
+ originalIndex: i,
1475
+ router: this
2345
1476
  });
2346
- const toMatches = router.matchRoutes(pathname);
2347
-
2348
- const prevParams = _extends({}, (_last = last(fromMatches)) == null ? void 0 : _last.params);
2349
-
2350
- let nextParams = ((_dest$params = dest.params) != null ? _dest$params : true) === true ? prevParams : functionalUpdate(dest.params, prevParams);
2351
-
2352
- if (nextParams) {
2353
- toMatches.map(d => d.options.stringifyParams).filter(Boolean).forEach(fn => {
2354
- Object.assign({}, nextParams, fn(nextParams));
2355
- });
1477
+ const existingRoute = this.routesById[route.id];
1478
+ invariant(!existingRoute, `Duplicate routes found with id: ${String(route.id)}`);
1479
+ this.routesById[route.id] = route;
1480
+ if (!route.isRoot && route.path) {
1481
+ const trimmedFullPath = trimPathRight(route.fullPath);
1482
+ if (!this.routesByPath[trimmedFullPath] || route.fullPath.endsWith('/')) {
1483
+ this.routesByPath[trimmedFullPath] = route;
1484
+ }
2356
1485
  }
2357
-
2358
- pathname = interpolatePath(pathname, nextParams != null ? nextParams : {}); // Pre filters first
2359
-
2360
- const preFilteredSearch = (_dest$__preSearchFilt = dest.__preSearchFilters) != null && _dest$__preSearchFilt.length ? dest.__preSearchFilters.reduce((prev, next) => next(prev), router.location.search) : router.location.search; // Then the link/navigate function
2361
-
2362
- const destSearch = dest.search === true ? preFilteredSearch // Preserve resolvedFrom true
2363
- : dest.search ? (_functionalUpdate = functionalUpdate(dest.search, preFilteredSearch)) != null ? _functionalUpdate : {} // Updater
2364
- : (_dest$__preSearchFilt2 = dest.__preSearchFilters) != null && _dest$__preSearchFilt2.length ? preFilteredSearch // Preserve resolvedFrom filters
2365
- : {}; // Then post filters
2366
-
2367
- const postFilteredSearch = (_dest$__postSearchFil = dest.__postSearchFilters) != null && _dest$__postSearchFil.length ? dest.__postSearchFilters.reduce((prev, next) => next(prev), destSearch) : destSearch;
2368
- const search = replaceEqualDeep(router.location.search, postFilteredSearch);
2369
- const searchStr = router.options.stringifySearch(search);
2370
- let hash = dest.hash === true ? router.location.hash : functionalUpdate(dest.hash, router.location.hash);
2371
- hash = hash ? "#" + hash : '';
2372
- return {
2373
- pathname,
2374
- search,
2375
- searchStr,
2376
- state: router.location.state,
2377
- hash,
2378
- href: "" + pathname + searchStr + hash,
2379
- key: dest.key
2380
- };
2381
- },
2382
- commitLocation: (next, replace) => {
2383
- const id = '' + Date.now() + Math.random();
2384
- if (router.navigateTimeout) clearTimeout(router.navigateTimeout);
2385
- let nextAction = 'replace';
2386
-
2387
- if (!replace) {
2388
- nextAction = 'push';
1486
+ const children = route.children;
1487
+ if (children?.length) {
1488
+ recurseRoutes(children);
2389
1489
  }
1490
+ });
1491
+ };
1492
+ recurseRoutes([routeTree]);
1493
+ this.flatRoutes = Object.values(this.routesByPath).map((d, i) => {
1494
+ const trimmed = trimPath(d.fullPath);
1495
+ const parsed = parsePathname(trimmed);
1496
+ while (parsed.length > 1 && parsed[0]?.value === '/') {
1497
+ parsed.shift();
1498
+ }
1499
+ const score = parsed.map(d => {
1500
+ if (d.type === 'param') {
1501
+ return 0.5;
1502
+ }
1503
+ if (d.type === 'wildcard') {
1504
+ return 0.25;
1505
+ }
1506
+ return 1;
1507
+ });
1508
+ return {
1509
+ child: d,
1510
+ trimmed,
1511
+ parsed,
1512
+ index: i,
1513
+ score
1514
+ };
1515
+ }).sort((a, b) => {
1516
+ let isIndex = a.trimmed === '/' ? 1 : b.trimmed === '/' ? -1 : 0;
1517
+ if (isIndex !== 0) return isIndex;
1518
+ const length = Math.min(a.score.length, b.score.length);
1519
+
1520
+ // Sort by length of score
1521
+ if (a.score.length !== b.score.length) {
1522
+ return b.score.length - a.score.length;
1523
+ }
2390
1524
 
2391
- const isSameUrl = router.__.parseLocation(history.location).href === next.href;
2392
-
2393
- if (isSameUrl && !next.key) {
2394
- nextAction = 'replace';
1525
+ // Sort by min available score
1526
+ for (let i = 0; i < length; i++) {
1527
+ if (a.score[i] !== b.score[i]) {
1528
+ return b.score[i] - a.score[i];
2395
1529
  }
1530
+ }
2396
1531
 
2397
- if (nextAction === 'replace') {
2398
- history.replace({
2399
- pathname: next.pathname,
2400
- hash: next.hash,
2401
- search: next.searchStr
2402
- }, {
2403
- id
2404
- });
2405
- } else {
2406
- history.push({
2407
- pathname: next.pathname,
2408
- hash: next.hash,
2409
- search: next.searchStr
2410
- }, {
2411
- id
2412
- });
1532
+ // Sort by min available parsed value
1533
+ for (let i = 0; i < length; i++) {
1534
+ if (a.parsed[i].value !== b.parsed[i].value) {
1535
+ return a.parsed[i].value > b.parsed[i].value ? 1 : -1;
2413
1536
  }
1537
+ }
2414
1538
 
2415
- router.navigationPromise = new Promise(resolve => {
2416
- const previousNavigationResolve = router.resolveNavigation;
1539
+ // Sort by length of trimmed full path
1540
+ if (a.trimmed !== b.trimmed) {
1541
+ return a.trimmed > b.trimmed ? 1 : -1;
1542
+ }
2417
1543
 
2418
- router.resolveNavigation = () => {
2419
- previousNavigationResolve();
2420
- resolve();
2421
- };
1544
+ // Sort by original index
1545
+ return a.index - b.index;
1546
+ }).map((d, i) => {
1547
+ d.child.rank = i;
1548
+ return d.child;
1549
+ });
1550
+ };
1551
+ #parseLocation = previousLocation => {
1552
+ let {
1553
+ pathname,
1554
+ search,
1555
+ hash,
1556
+ state
1557
+ } = this.history.location;
1558
+ const parsedSearch = this.options.parseSearch(search);
1559
+ return {
1560
+ pathname: pathname,
1561
+ searchStr: search,
1562
+ search: replaceEqualDeep(previousLocation?.search, parsedSearch),
1563
+ hash: hash.split('#').reverse()[0] ?? '',
1564
+ href: `${pathname}${search}${hash}`,
1565
+ state: state,
1566
+ key: state?.key || '__init__'
1567
+ };
1568
+ };
1569
+ #buildLocation = (dest = {}) => {
1570
+ dest.fromCurrent = dest.fromCurrent ?? dest.to === '';
1571
+ const fromPathname = dest.fromCurrent ? this.state.location.pathname : dest.from ?? this.state.location.pathname;
1572
+ let pathname = resolvePath(this.basepath ?? '/', fromPathname, `${dest.to ?? ''}`);
1573
+ const fromMatches = this.matchRoutes(this.state.location.pathname, this.state.location.search);
1574
+ const prevParams = {
1575
+ ...last(fromMatches)?.params
1576
+ };
1577
+ let nextParams = (dest.params ?? true) === true ? prevParams : functionalUpdate(dest.params, prevParams);
1578
+ if (nextParams) {
1579
+ dest.__matches?.map(d => this.getRoute(d.routeId).options.stringifyParams).filter(Boolean).forEach(fn => {
1580
+ nextParams = {
1581
+ ...nextParams,
1582
+ ...fn(nextParams)
1583
+ };
1584
+ });
1585
+ }
1586
+ pathname = interpolatePath(pathname, nextParams ?? {});
1587
+ const preSearchFilters = dest.__matches?.map(match => this.getRoute(match.routeId).options.preSearchFilters ?? []).flat().filter(Boolean) ?? [];
1588
+ const postSearchFilters = dest.__matches?.map(match => this.getRoute(match.routeId).options.postSearchFilters ?? []).flat().filter(Boolean) ?? [];
1589
+
1590
+ // Pre filters first
1591
+ const preFilteredSearch = preSearchFilters?.length ? preSearchFilters?.reduce((prev, next) => next(prev), this.state.location.search) : this.state.location.search;
1592
+
1593
+ // Then the link/navigate function
1594
+ const destSearch = dest.search === true ? preFilteredSearch // Preserve resolvedFrom true
1595
+ : dest.search ? functionalUpdate(dest.search, preFilteredSearch) ?? {} // Updater
1596
+ : preSearchFilters?.length ? preFilteredSearch // Preserve resolvedFrom filters
1597
+ : {};
1598
+
1599
+ // Then post filters
1600
+ const postFilteredSearch = postSearchFilters?.length ? postSearchFilters.reduce((prev, next) => next(prev), destSearch) : destSearch;
1601
+ const search = replaceEqualDeep(this.state.location.search, postFilteredSearch);
1602
+ const searchStr = this.options.stringifySearch(search);
1603
+ const hash = dest.hash === true ? this.state.location.hash : functionalUpdate(dest.hash, this.state.location.hash);
1604
+ const hashStr = hash ? `#${hash}` : '';
1605
+ const nextState = dest.state === true ? this.state.location.state : functionalUpdate(dest.state, this.state.location.state);
1606
+ return {
1607
+ pathname,
1608
+ search,
1609
+ searchStr,
1610
+ state: nextState,
1611
+ hash,
1612
+ href: this.history.createHref(`${pathname}${searchStr}${hashStr}`),
1613
+ key: dest.key
1614
+ };
1615
+ };
1616
+ #commitLocation = async location => {
1617
+ const next = this.buildNext(location);
1618
+ const id = '' + Date.now() + Math.random();
1619
+ if (this.navigateTimeout) clearTimeout(this.navigateTimeout);
1620
+ let nextAction = 'replace';
1621
+ if (!location.replace) {
1622
+ nextAction = 'push';
1623
+ }
1624
+ const isSameUrl = this.state.location.href === next.href;
1625
+ if (isSameUrl && !next.key) {
1626
+ nextAction = 'replace';
1627
+ }
1628
+ const href = `${next.pathname}${next.searchStr}${next.hash ? `#${next.hash}` : ''}`;
1629
+ this.history[nextAction === 'push' ? 'push' : 'replace'](href, {
1630
+ id,
1631
+ ...next.state
1632
+ });
1633
+ return this.latestLoadPromise;
1634
+ };
1635
+ getRouteMatch = id => {
1636
+ return this.state.matchesById[id];
1637
+ };
1638
+ setRouteMatch = (id, updater) => {
1639
+ this.__store.setState(prev => ({
1640
+ ...prev,
1641
+ matchesById: {
1642
+ ...prev.matchesById,
1643
+ [id]: updater(prev.matchesById[id])
1644
+ }
1645
+ }));
1646
+ };
1647
+ setRouteMatchData = (id, updater, opts) => {
1648
+ const match = this.getRouteMatch(id);
1649
+ if (!match) return;
1650
+ const route = this.getRoute(match.routeId);
1651
+ const updatedAt = opts?.updatedAt ?? Date.now();
1652
+ const preloadInvalidAt = updatedAt + (opts?.maxAge ?? route.options.preloadMaxAge ?? this.options.defaultPreloadMaxAge ?? 5000);
1653
+ const invalidAt = updatedAt + (opts?.maxAge ?? route.options.maxAge ?? this.options.defaultMaxAge ?? Infinity);
1654
+ this.setRouteMatch(id, s => ({
1655
+ ...s,
1656
+ error: undefined,
1657
+ status: 'success',
1658
+ isFetching: false,
1659
+ updatedAt: Date.now(),
1660
+ loaderData: functionalUpdate(updater, s.loaderData),
1661
+ preloadInvalidAt,
1662
+ invalidAt
1663
+ }));
1664
+ if (this.state.matches.find(d => d.id === id)) ;
1665
+ };
1666
+ invalidate = async opts => {
1667
+ if (opts?.matchId) {
1668
+ this.setRouteMatch(opts.matchId, s => ({
1669
+ ...s,
1670
+ invalid: true
1671
+ }));
1672
+ const matchIndex = this.state.matches.findIndex(d => d.id === opts.matchId);
1673
+ const childMatch = this.state.matches[matchIndex + 1];
1674
+ if (childMatch) {
1675
+ return this.invalidate({
1676
+ matchId: childMatch.id,
1677
+ reload: false
2422
1678
  });
2423
- return router.navigationPromise;
2424
1679
  }
1680
+ } else {
1681
+ this.__store.batch(() => {
1682
+ Object.values(this.state.matchesById).forEach(match => {
1683
+ this.setRouteMatch(match.id, s => ({
1684
+ ...s,
1685
+ invalid: true
1686
+ }));
1687
+ });
1688
+ });
1689
+ }
1690
+ if (opts?.reload ?? true) {
1691
+ return this.reload();
2425
1692
  }
2426
1693
  };
2427
- router.update(userOptions); // Allow frameworks to hook into the router creation
2428
-
2429
- router.options.createRouter == null ? void 0 : router.options.createRouter(router);
2430
- return router;
1694
+ getIsInvalid = opts => {
1695
+ if (!opts?.matchId) {
1696
+ return !!this.state.matches.find(d => this.getIsInvalid({
1697
+ matchId: d.id,
1698
+ preload: opts?.preload
1699
+ }));
1700
+ }
1701
+ const match = this.getRouteMatch(opts?.matchId);
1702
+ if (!match) {
1703
+ return false;
1704
+ }
1705
+ const now = Date.now();
1706
+ return match.invalid || (opts?.preload ? match.preloadInvalidAt : match.invalidAt) < now;
1707
+ };
2431
1708
  }
2432
1709
 
1710
+ // Detect if we're in the DOM
1711
+ const isServer = typeof window === 'undefined' || !window.document.createElement;
1712
+ function getInitialRouterState() {
1713
+ return {
1714
+ status: 'idle',
1715
+ isFetching: false,
1716
+ resolvedLocation: null,
1717
+ location: null,
1718
+ matchesById: {},
1719
+ matchIds: [],
1720
+ pendingMatchIds: [],
1721
+ matches: [],
1722
+ pendingMatches: [],
1723
+ lastUpdated: Date.now()
1724
+ };
1725
+ }
2433
1726
  function isCtrlEvent(e) {
2434
1727
  return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
2435
1728
  }
1729
+ function redirect(opts) {
1730
+ opts.isRedirect = true;
1731
+ return opts;
1732
+ }
1733
+ function isRedirect(obj) {
1734
+ return !!obj?.isRedirect;
1735
+ }
1736
+ class SearchParamError extends Error {}
1737
+ class PathParamError extends Error {}
1738
+ function escapeJSON(jsonString) {
1739
+ return jsonString.replace(/\\/g, '\\\\') // Escape backslashes
1740
+ .replace(/'/g, "\\'") // Escape single quotes
1741
+ .replace(/"/g, '\\"'); // Escape double quotes
1742
+ }
2436
1743
 
2437
- function cascadeLoaderData(matches) {
2438
- matches.forEach((match, index) => {
2439
- const parent = matches[index - 1];
2440
-
2441
- if (parent) {
2442
- match.loaderData = replaceEqualDeep(match.loaderData, _extends({}, parent.loaderData, match.routeLoaderData));
2443
- }
2444
- });
1744
+ // A function that takes an import() argument which is a function and returns a new function that will
1745
+ // proxy arguments from the caller to the imported function, retaining all type
1746
+ // information along the way
1747
+ function lazyFn(fn, key) {
1748
+ return async (...args) => {
1749
+ const imported = await fn();
1750
+ return imported[key || 'default'](...args);
1751
+ };
2445
1752
  }
2446
1753
 
2447
- 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 };
1754
+ 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 };
2448
1755
  //# sourceMappingURL=index.js.map