@tanstack/router-core 0.0.1-beta.13 → 0.0.1-beta.145

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