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

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