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

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