@tanstack/react-router 0.0.1-beta.46 → 0.0.1-beta.49

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.
@@ -9,10 +9,10 @@
9
9
  * @license MIT
10
10
  */
11
11
  (function (global, factory) {
12
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react'), require('use-sync-external-store/shim')) :
13
- typeof define === 'function' && define.amd ? define(['exports', 'react', 'use-sync-external-store/shim'], factory) :
14
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ReactRouter = {}, global.React, global.shim));
15
- })(this, (function (exports, React, shim) { 'use strict';
12
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react'), require('use-sync-external-store/shim/with-selector')) :
13
+ typeof define === 'function' && define.amd ? define(['exports', 'react', 'use-sync-external-store/shim/with-selector'], factory) :
14
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ReactRouter = {}, global.React, global.withSelector));
15
+ })(this, (function (exports, React, withSelector) { 'use strict';
16
16
 
17
17
  function _interopNamespace(e) {
18
18
  if (e && e.__esModule) return e;
@@ -34,414 +34,6 @@
34
34
 
35
35
  var React__namespace = /*#__PURE__*/_interopNamespace(React);
36
36
 
37
- function _extends$1() {
38
- _extends$1 = Object.assign ? Object.assign.bind() : function (target) {
39
- for (var i = 1; i < arguments.length; i++) {
40
- var source = arguments[i];
41
- for (var key in source) {
42
- if (Object.prototype.hasOwnProperty.call(source, key)) {
43
- target[key] = source[key];
44
- }
45
- }
46
- }
47
- return target;
48
- };
49
- return _extends$1.apply(this, arguments);
50
- }
51
-
52
- // src/core.ts
53
- var CurrentReaction = void 0;
54
- var CurrentGets = null;
55
- var CurrentGetsIndex = 0;
56
- var EffectQueue = null;
57
- var CacheClean = 0;
58
- var CacheCheck = 1;
59
- var CacheDirty = 2;
60
- var Root;
61
- var Reactive = class {
62
- value;
63
- fn;
64
- observers = null;
65
- sources = null;
66
- state;
67
- effect;
68
- cleanups = null;
69
- alwaysUpdate = false;
70
- constructor(fnOrValue, type) {
71
- if (type != 0 /* Signal */) {
72
- this.fn = fnOrValue;
73
- this.value = void 0;
74
- this.state = CacheDirty;
75
- if (Root)
76
- Root.push(this);
77
- else
78
- console.error("Memos and effects must be wrapped in a createRoot");
79
- this.effect = type == 2 /* Effect */;
80
- if (this.effect)
81
- this.update();
82
- } else {
83
- this.fn = void 0;
84
- this.value = fnOrValue;
85
- this.state = CacheClean;
86
- this.effect = false;
87
- }
88
- }
89
- get() {
90
- if (CurrentReaction) {
91
- if (!CurrentGets && CurrentReaction.sources && CurrentReaction.sources[CurrentGetsIndex] == this) {
92
- CurrentGetsIndex++;
93
- } else {
94
- if (!CurrentGets)
95
- CurrentGets = [this];
96
- else
97
- CurrentGets.push(this);
98
- }
99
- }
100
- if (this.fn)
101
- this.updateIfNecessary();
102
- return this.value;
103
- }
104
- set(value) {
105
- const notInBatch = !EffectQueue;
106
- const newValue = typeof value === "function" ? value(this.value) : value;
107
- if ((this.value !== newValue || this.alwaysUpdate) && this.observers) {
108
- for (let i = 0; i < this.observers.length; i++) {
109
- this.observers[i].stale(CacheDirty);
110
- }
111
- }
112
- this.value = newValue;
113
- if (notInBatch)
114
- stabilize();
115
- return newValue;
116
- }
117
- stale(state) {
118
- if (this.state < state) {
119
- if (this.state === CacheClean && this.effect) {
120
- if (EffectQueue)
121
- EffectQueue.push(this);
122
- else
123
- EffectQueue = [this];
124
- }
125
- this.state = state;
126
- if (this.observers) {
127
- for (let i = 0; i < this.observers.length; i++) {
128
- this.observers[i].stale(CacheCheck);
129
- }
130
- }
131
- }
132
- }
133
- update() {
134
- const oldValue = this.value;
135
- const prevReaction = CurrentReaction;
136
- const prevGets = CurrentGets;
137
- const prevIndex = CurrentGetsIndex;
138
- CurrentReaction = this;
139
- CurrentGets = null;
140
- CurrentGetsIndex = 0;
141
- try {
142
- if (this.cleanups) {
143
- this.cleanups.forEach((c) => c());
144
- this.cleanups = null;
145
- }
146
- this.value = this.fn();
147
- if (CurrentGets) {
148
- this.removeParentObservers(CurrentGetsIndex);
149
- if (this.sources && CurrentGetsIndex > 0) {
150
- this.sources.length = CurrentGetsIndex + CurrentGets.length;
151
- for (let i = 0; i < CurrentGets.length; i++) {
152
- this.sources[CurrentGetsIndex + i] = CurrentGets[i];
153
- }
154
- } else {
155
- this.sources = CurrentGets;
156
- }
157
- for (let i = CurrentGetsIndex; i < this.sources.length; i++) {
158
- const source = this.sources[i];
159
- if (!source.observers) {
160
- source.observers = [this];
161
- } else {
162
- source.observers.push(this);
163
- }
164
- }
165
- } else if (this.sources && CurrentGetsIndex < this.sources.length) {
166
- this.removeParentObservers(CurrentGetsIndex);
167
- this.sources.length = CurrentGetsIndex;
168
- }
169
- } finally {
170
- CurrentGets = prevGets;
171
- CurrentReaction = prevReaction;
172
- CurrentGetsIndex = prevIndex;
173
- }
174
- if ((oldValue !== this.value || this.alwaysUpdate) && this.observers) {
175
- for (let i = 0; i < this.observers.length; i++) {
176
- this.observers[i].state = CacheDirty;
177
- }
178
- }
179
- this.state = CacheClean;
180
- }
181
- updateIfNecessary() {
182
- if (this.state === CacheCheck) {
183
- for (const source of this.sources) {
184
- source.updateIfNecessary();
185
- if (this.state === CacheDirty) {
186
- break;
187
- }
188
- }
189
- }
190
- if (this.state === CacheDirty) {
191
- this.update();
192
- }
193
- this.state = CacheClean;
194
- }
195
- removeParentObservers(index) {
196
- if (!this.sources)
197
- return;
198
- for (let i = index; i < this.sources.length; i++) {
199
- const source = this.sources[i];
200
- const swap = source.observers.findIndex((v) => v === this);
201
- source.observers[swap] = source.observers[source.observers.length - 1];
202
- source.observers.pop();
203
- }
204
- }
205
- destroy() {
206
- if (this.cleanups) {
207
- this.cleanups.forEach((c) => c());
208
- this.cleanups = null;
209
- }
210
- this.removeParentObservers(0);
211
- }
212
- };
213
- function onCleanup(fn) {
214
- if (CurrentReaction) {
215
- if (!CurrentReaction.cleanups)
216
- CurrentReaction.cleanups = [fn];
217
- else
218
- CurrentReaction.cleanups.push(fn);
219
- } else {
220
- console.error("onCleanup must be called from within a memo or effect");
221
- }
222
- }
223
- function stabilize() {
224
- if (!EffectQueue)
225
- return;
226
- for (let i = 0; i < EffectQueue.length; i++) {
227
- EffectQueue[i].get();
228
- }
229
- EffectQueue = null;
230
- }
231
- function createSignal(value, options) {
232
- const signal = new Reactive(value, 0 /* Signal */);
233
- if (options?.equals !== void 0)
234
- signal.alwaysUpdate = true;
235
- return [signal.get.bind(signal), signal.set.bind(signal)];
236
- }
237
- function createMemo(fn) {
238
- const memo = new Reactive(fn, 1 /* Memo */);
239
- return memo.get.bind(memo);
240
- }
241
- function createEffect(fn) {
242
- const effect = new Reactive(fn, 2 /* Effect */);
243
- return effect.get.bind(effect);
244
- }
245
- function createRoot(fn) {
246
- let root = [];
247
- Root = root;
248
- fn();
249
- Root = null;
250
- return () => {
251
- if (!root)
252
- return;
253
- root.forEach((r) => r.destroy());
254
- root = null;
255
- };
256
- }
257
- function batch(fn) {
258
- EffectQueue = [];
259
- let out = fn();
260
- stabilize();
261
- return out;
262
- }
263
- function untrack(fn) {
264
- const listener = CurrentReaction;
265
- CurrentReaction = void 0;
266
- try {
267
- return fn();
268
- } finally {
269
- CurrentReaction = listener;
270
- }
271
- }
272
-
273
- // src/store.ts
274
- var $RAW = Symbol("store-raw");
275
- var $TRACK = Symbol("track");
276
- var $PROXY = Symbol("store-proxy");
277
- var $NODE = Symbol("store-node");
278
- function wrap(value) {
279
- let p = value[$PROXY];
280
- if (!p) {
281
- Object.defineProperty(value, $PROXY, {
282
- value: p = new Proxy(value, proxyTraps)
283
- });
284
- if (!Array.isArray(value)) {
285
- const keys = Object.keys(value);
286
- const desc = Object.getOwnPropertyDescriptors(value);
287
- for (let i = 0, l = keys.length; i < l; i++) {
288
- const prop = keys[i];
289
- if (desc[prop].get) {
290
- const get = desc[prop].get.bind(p);
291
- Object.defineProperty(value, prop, {
292
- enumerable: desc[prop].enumerable,
293
- get
294
- });
295
- }
296
- }
297
- }
298
- }
299
- return p;
300
- }
301
- function isWrappable(obj) {
302
- let proto;
303
- return obj != null && typeof obj === "object" && (obj[$PROXY] || !(proto = Object.getPrototypeOf(obj)) || proto === Object.prototype || Array.isArray(obj));
304
- }
305
- function unwrap(item, set = /* @__PURE__ */ new Set()) {
306
- let result, unwrapped, v, prop;
307
- if (result = item != null && item[$RAW])
308
- return result;
309
- if (!isWrappable(item) || set.has(item))
310
- return item;
311
- if (Array.isArray(item)) {
312
- if (Object.isFrozen(item))
313
- item = item.slice(0);
314
- else
315
- set.add(item);
316
- for (let i = 0, l = item.length; i < l; i++) {
317
- v = item[i];
318
- if ((unwrapped = unwrap(v, set)) !== v)
319
- item[i] = unwrapped;
320
- }
321
- } else {
322
- if (Object.isFrozen(item))
323
- item = Object.assign({}, item);
324
- else
325
- set.add(item);
326
- const keys = Object.keys(item);
327
- const desc = Object.getOwnPropertyDescriptors(item);
328
- for (let i = 0, l = keys.length; i < l; i++) {
329
- prop = keys[i];
330
- if (desc[prop].get)
331
- continue;
332
- v = item[prop];
333
- if ((unwrapped = unwrap(v, set)) !== v)
334
- item[prop] = unwrapped;
335
- }
336
- }
337
- return item;
338
- }
339
- function getDataNodes(target) {
340
- let nodes = target[$NODE];
341
- if (!nodes)
342
- Object.defineProperty(target, $NODE, { value: nodes = {} });
343
- return nodes;
344
- }
345
- function getDataNode(nodes, property, value) {
346
- return nodes[property] || (nodes[property] = createDataNode(value));
347
- }
348
- function proxyDescriptor(target, property) {
349
- const desc = Reflect.getOwnPropertyDescriptor(target, property);
350
- if (!desc || desc.get || !desc.configurable || property === $PROXY || property === $NODE)
351
- return desc;
352
- delete desc.value;
353
- delete desc.writable;
354
- desc.get = () => target[$PROXY][property];
355
- return desc;
356
- }
357
- function trackSelf(target) {
358
- if (CurrentReaction) {
359
- const nodes = getDataNodes(target);
360
- (nodes._ || (nodes._ = createDataNode())).get();
361
- }
362
- }
363
- function ownKeys(target) {
364
- trackSelf(target);
365
- return Reflect.ownKeys(target);
366
- }
367
- function createDataNode(value) {
368
- const s = new Reactive(value, 0);
369
- s.alwaysUpdate = true;
370
- return s;
371
- }
372
- var Writing = false;
373
- var proxyTraps = {
374
- get(target, property, receiver) {
375
- if (property === $RAW)
376
- return target;
377
- if (property === $PROXY)
378
- return receiver;
379
- if (property === $TRACK) {
380
- trackSelf(target);
381
- return receiver;
382
- }
383
- const nodes = getDataNodes(target);
384
- const tracked = nodes.hasOwnProperty(property);
385
- let value = tracked ? nodes[property].get() : target[property];
386
- if (property === $NODE || property === "__proto__")
387
- return value;
388
- if (!tracked) {
389
- const desc = Object.getOwnPropertyDescriptor(target, property);
390
- if (CurrentReaction && (typeof value !== "function" || target.hasOwnProperty(property)) && !(desc && desc.get))
391
- value = getDataNode(nodes, property, value).get();
392
- }
393
- return isWrappable(value) ? wrap(value) : value;
394
- },
395
- has(target, property) {
396
- if (property === $RAW || property === $PROXY || property === $TRACK || property === $NODE || property === "__proto__")
397
- return true;
398
- this.get(target, property, target);
399
- return property in target;
400
- },
401
- set(target, property, value) {
402
- Writing && setProperty(target, property, unwrap(value));
403
- return true;
404
- },
405
- deleteProperty(target, property) {
406
- Writing && setProperty(target, property, void 0, true);
407
- return true;
408
- },
409
- ownKeys,
410
- getOwnPropertyDescriptor: proxyDescriptor
411
- };
412
- function setProperty(state, property, value, deleting = false) {
413
- if (!deleting && state[property] === value)
414
- return;
415
- const prev = state[property];
416
- const len = state.length;
417
- if (deleting)
418
- delete state[property];
419
- else
420
- state[property] = value;
421
- const nodes = getDataNodes(state);
422
- let node;
423
- if (node = getDataNode(nodes, property, prev))
424
- node.set(() => value);
425
- if (Array.isArray(state) && state.length !== len)
426
- (node = getDataNode(nodes, "length", len)) && node.set(state.length);
427
- (node = nodes._) && node.set();
428
- }
429
- function createStore(store) {
430
- const unwrappedStore = unwrap(store);
431
- const wrappedStore = wrap(unwrappedStore);
432
- const setStore = (fn) => {
433
- batch(() => {
434
- try {
435
- Writing = true;
436
- fn(wrappedStore);
437
- } finally {
438
- Writing = false;
439
- }
440
- });
441
- };
442
- return [wrappedStore, setStore];
443
- }
444
-
445
37
  function _extends() {
446
38
  _extends = Object.assign ? Object.assign.bind() : function (target) {
447
39
  for (var i = 1; i < arguments.length; i++) {
@@ -457,818 +49,174 @@
457
49
  return _extends.apply(this, arguments);
458
50
  }
459
51
 
460
- /**
461
- * Actions represent the type of change to a location value.
462
- *
463
- * @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#action
464
- */
465
- var Action;
466
-
467
- (function (Action) {
468
- /**
469
- * A POP indicates a change to an arbitrary index in the history stack, such
470
- * as a back or forward navigation. It does not describe the direction of the
471
- * navigation, only that the current index changed.
472
- *
473
- * Note: This is the default action for newly created history objects.
474
- */
475
- Action["Pop"] = "POP";
476
- /**
477
- * A PUSH indicates a new entry being added to the history stack, such as when
478
- * a link is clicked and a new page loads. When this happens, all subsequent
479
- * entries in the stack are lost.
480
- */
481
-
482
- Action["Push"] = "PUSH";
483
- /**
484
- * A REPLACE indicates the entry at the current index in the history stack
485
- * being replaced by a new one.
486
- */
487
-
488
- Action["Replace"] = "REPLACE";
489
- })(Action || (Action = {}));
490
-
491
- var readOnly = function (obj) {
492
- return Object.freeze(obj);
493
- } ;
494
-
495
- function warning$1(cond, message) {
496
- if (!cond) {
497
- // eslint-disable-next-line no-console
498
- if (typeof console !== 'undefined') console.warn(message);
499
-
500
- try {
501
- // Welcome to debugging history!
502
- //
503
- // This error is thrown as a convenience so you can more easily
504
- // find the source for a warning that appears in the console by
505
- // enabling "pause on exceptions" in your JavaScript debugger.
506
- throw new Error(message); // eslint-disable-next-line no-empty
507
- } catch (e) {}
508
- }
509
- }
510
-
511
- var BeforeUnloadEventType = 'beforeunload';
512
- var HashChangeEventType = 'hashchange';
513
- var PopStateEventType = 'popstate';
514
- /**
515
- * Browser history stores the location in regular URLs. This is the standard for
516
- * most web apps, but it requires some configuration on the server to ensure you
517
- * serve the same app at multiple URLs.
518
- *
519
- * @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#createbrowserhistory
520
- */
521
-
522
- function createBrowserHistory(options) {
523
- if (options === void 0) {
524
- options = {};
525
- }
526
-
527
- var _options = options,
528
- _options$window = _options.window,
529
- window = _options$window === void 0 ? document.defaultView : _options$window;
530
- var globalHistory = window.history;
531
-
532
- function getIndexAndLocation() {
533
- var _window$location = window.location,
534
- pathname = _window$location.pathname,
535
- search = _window$location.search,
536
- hash = _window$location.hash;
537
- var state = globalHistory.state || {};
538
- return [state.idx, readOnly({
539
- pathname: pathname,
540
- search: search,
541
- hash: hash,
542
- state: state.usr || null,
543
- key: state.key || 'default'
544
- })];
545
- }
546
-
547
- var blockedPopTx = null;
548
-
549
- function handlePop() {
550
- if (blockedPopTx) {
551
- blockers.call(blockedPopTx);
552
- blockedPopTx = null;
553
- } else {
554
- var nextAction = Action.Pop;
555
-
556
- var _getIndexAndLocation = getIndexAndLocation(),
557
- nextIndex = _getIndexAndLocation[0],
558
- nextLocation = _getIndexAndLocation[1];
559
-
560
- if (blockers.length) {
561
- if (nextIndex != null) {
562
- var delta = index - nextIndex;
563
-
564
- if (delta) {
565
- // Revert the POP
566
- blockedPopTx = {
567
- action: nextAction,
568
- location: nextLocation,
569
- retry: function retry() {
570
- go(delta * -1);
571
- }
572
- };
573
- go(delta);
574
- }
575
- } else {
576
- // Trying to POP to a location with no index. We did not create
577
- // this location, so we can't effectively block the navigation.
578
- warning$1(false, // TODO: Write up a doc that explains our blocking strategy in
579
- // detail and link to it here so people can understand better what
580
- // is going on and how to avoid it.
581
- "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.") ;
582
- }
583
- } else {
584
- applyTx(nextAction);
585
- }
586
- }
587
- }
588
-
589
- window.addEventListener(PopStateEventType, handlePop);
590
- var action = Action.Pop;
591
-
592
- var _getIndexAndLocation2 = getIndexAndLocation(),
593
- index = _getIndexAndLocation2[0],
594
- location = _getIndexAndLocation2[1];
595
-
596
- var listeners = createEvents();
597
- var blockers = createEvents();
598
-
599
- if (index == null) {
600
- index = 0;
601
- globalHistory.replaceState(_extends({}, globalHistory.state, {
602
- idx: index
603
- }), '');
604
- }
605
-
606
- function createHref(to) {
607
- return typeof to === 'string' ? to : createPath(to);
608
- } // state defaults to `null` because `window.history.state` does
609
-
610
-
611
- function getNextLocation(to, state) {
612
- if (state === void 0) {
613
- state = null;
614
- }
615
-
616
- return readOnly(_extends({
617
- pathname: location.pathname,
618
- hash: '',
619
- search: ''
620
- }, typeof to === 'string' ? parsePath(to) : to, {
621
- state: state,
622
- key: createKey()
623
- }));
624
- }
625
-
626
- function getHistoryStateAndUrl(nextLocation, index) {
627
- return [{
628
- usr: nextLocation.state,
629
- key: nextLocation.key,
630
- idx: index
631
- }, createHref(nextLocation)];
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) {
643
- action = nextAction;
644
-
645
- var _getIndexAndLocation3 = getIndexAndLocation();
646
-
647
- index = _getIndexAndLocation3[0];
648
- location = _getIndexAndLocation3[1];
649
- listeners.call({
650
- action: action,
651
- location: location
652
- });
653
- }
654
-
655
- function push(to, state) {
656
- var nextAction = Action.Push;
657
- var nextLocation = getNextLocation(to, state);
658
-
659
- function retry() {
660
- push(to, state);
661
- }
662
-
663
- if (allowTx(nextAction, nextLocation, retry)) {
664
- var _getHistoryStateAndUr = getHistoryStateAndUrl(nextLocation, index + 1),
665
- historyState = _getHistoryStateAndUr[0],
666
- url = _getHistoryStateAndUr[1]; // TODO: Support forced reloading
667
- // try...catch because iOS limits us to 100 pushState calls :/
668
-
669
-
670
- try {
671
- globalHistory.pushState(historyState, '', url);
672
- } catch (error) {
673
- // They are going to lose state here, but there is no real
674
- // way to warn them about it since the page will refresh...
675
- window.location.assign(url);
676
- }
677
-
678
- applyTx(nextAction);
679
- }
680
- }
681
-
682
- function replace(to, state) {
683
- var nextAction = Action.Replace;
684
- var nextLocation = getNextLocation(to, state);
685
-
686
- function retry() {
687
- replace(to, state);
688
- }
689
-
690
- if (allowTx(nextAction, nextLocation, retry)) {
691
- var _getHistoryStateAndUr2 = getHistoryStateAndUrl(nextLocation, index),
692
- historyState = _getHistoryStateAndUr2[0],
693
- url = _getHistoryStateAndUr2[1]; // TODO: Support forced reloading
694
-
695
-
696
- globalHistory.replaceState(historyState, '', url);
697
- applyTx(nextAction);
52
+ var prefix = 'Invariant failed';
53
+ function invariant(condition, message) {
54
+ if (condition) {
55
+ return;
698
56
  }
699
- }
57
+ var provided = typeof message === 'function' ? message() : message;
58
+ var value = provided ? "".concat(prefix, ": ").concat(provided) : prefix;
59
+ throw new Error(value);
60
+ }
700
61
 
701
- function go(delta) {
702
- globalHistory.go(delta);
703
- }
62
+ function n(n){for(var r=arguments.length,t=Array(r>1?r-1:0),e=1;e<r;e++)t[e-1]=arguments[e];{var i=Y[n],o=i?"function"==typeof i?i.apply(null,t):i:"unknown error nr: "+n;throw Error("[Immer] "+o)}}function r(n){return !!n&&!!n[Q]}function t(n){var r;return !!n&&(function(n){if(!n||"object"!=typeof n)return !1;var r=Object.getPrototypeOf(n);if(null===r)return !0;var t=Object.hasOwnProperty.call(r,"constructor")&&r.constructor;return t===Object||"function"==typeof t&&Function.toString.call(t)===Z}(n)||Array.isArray(n)||!!n[L]||!!(null===(r=n.constructor)||void 0===r?void 0:r[L])||s(n)||v(n))}function i(n,r,t){void 0===t&&(t=!1),0===o(n)?(t?Object.keys:nn)(n).forEach((function(e){t&&"symbol"==typeof e||r(e,n[e],n);})):n.forEach((function(t,e){return r(e,t,n)}));}function o(n){var r=n[Q];return r?r.i>3?r.i-4:r.i:Array.isArray(n)?1:s(n)?2:v(n)?3:0}function u(n,r){return 2===o(n)?n.has(r):Object.prototype.hasOwnProperty.call(n,r)}function a(n,r){return 2===o(n)?n.get(r):n[r]}function f(n,r,t){var e=o(n);2===e?n.set(r,t):3===e?(n.delete(r),n.add(t)):n[r]=t;}function c(n,r){return n===r?0!==n||1/n==1/r:n!=n&&r!=r}function s(n){return X&&n instanceof Map}function v(n){return q&&n instanceof Set}function p(n){return n.o||n.t}function l(n){if(Array.isArray(n))return Array.prototype.slice.call(n);var r=rn(n);delete r[Q];for(var t=nn(r),e=0;e<t.length;e++){var i=t[e],o=r[i];!1===o.writable&&(o.writable=!0,o.configurable=!0),(o.get||o.set)&&(r[i]={configurable:!0,writable:!0,enumerable:o.enumerable,value:n[i]});}return Object.create(Object.getPrototypeOf(n),r)}function d(n,e){return void 0===e&&(e=!1),y(n)||r(n)||!t(n)?n:(o(n)>1&&(n.set=n.add=n.clear=n.delete=h),Object.freeze(n),e&&i(n,(function(n,r){return d(r,!0)}),!0),n)}function h(){n(2);}function y(n){return null==n||"object"!=typeof n||Object.isFrozen(n)}function b(r){var t=tn[r];return t||n(18,r),t}function _(){return U||n(0),U}function j(n,r){r&&(b("Patches"),n.u=[],n.s=[],n.v=r);}function O(n){g(n),n.p.forEach(S),n.p=null;}function g(n){n===U&&(U=n.l);}function w(n){return U={p:[],l:U,h:n,m:!0,_:0}}function S(n){var r=n[Q];0===r.i||1===r.i?r.j():r.O=!0;}function P(r,e){e._=e.p.length;var i=e.p[0],o=void 0!==r&&r!==i;return e.h.g||b("ES5").S(e,r,o),o?(i[Q].P&&(O(e),n(4)),t(r)&&(r=M(e,r),e.l||x(e,r)),e.u&&b("Patches").M(i[Q].t,r,e.u,e.s)):r=M(e,i,[]),O(e),e.u&&e.v(e.u,e.s),r!==H?r:void 0}function M(n,r,t){if(y(r))return r;var e=r[Q];if(!e)return i(r,(function(i,o){return A(n,e,r,i,o,t)}),!0),r;if(e.A!==n)return r;if(!e.P)return x(n,e.t,!0),e.t;if(!e.I){e.I=!0,e.A._--;var o=4===e.i||5===e.i?e.o=l(e.k):e.o;i(3===e.i?new Set(o):o,(function(r,i){return A(n,e,o,r,i,t)})),x(n,o,!1),t&&n.u&&b("Patches").R(e,t,n.u,n.s);}return e.o}function A(e,i,o,a,c,s){if(c===o&&n(5),r(c)){var v=M(e,c,s&&i&&3!==i.i&&!u(i.D,a)?s.concat(a):void 0);if(f(o,a,v),!r(v))return;e.m=!1;}if(t(c)&&!y(c)){if(!e.h.F&&e._<1)return;M(e,c),i&&i.A.l||x(e,c);}}function x(n,r,t){void 0===t&&(t=!1),n.h.F&&n.m&&d(r,t);}function z(n,r){var t=n[Q];return (t?p(t):n)[r]}function I(n,r){if(r in n)for(var t=Object.getPrototypeOf(n);t;){var e=Object.getOwnPropertyDescriptor(t,r);if(e)return e;t=Object.getPrototypeOf(t);}}function k(n){n.P||(n.P=!0,n.l&&k(n.l));}function E(n){n.o||(n.o=l(n.t));}function R(n,r,t){var e=s(r)?b("MapSet").N(r,t):v(r)?b("MapSet").T(r,t):n.g?function(n,r){var t=Array.isArray(n),e={i:t?1:0,A:r?r.A:_(),P:!1,I:!1,D:{},l:r,t:n,k:null,o:null,j:null,C:!1},i=e,o=en;t&&(i=[e],o=on);var u=Proxy.revocable(i,o),a=u.revoke,f=u.proxy;return e.k=f,e.j=a,f}(r,t):b("ES5").J(r,t);return (t?t.A:_()).p.push(e),e}function D(e){return r(e)||n(22,e),function n(r){if(!t(r))return r;var e,u=r[Q],c=o(r);if(u){if(!u.P&&(u.i<4||!b("ES5").K(u)))return u.t;u.I=!0,e=F(r,c),u.I=!1;}else e=F(r,c);return i(e,(function(r,t){u&&a(u.t,r)===t||f(e,r,n(t));})),3===c?new Set(e):e}(e)}function F(n,r){switch(r){case 2:return new Map(n);case 3:return Array.from(n)}return l(n)}var G,U,W="undefined"!=typeof Symbol&&"symbol"==typeof Symbol("x"),X="undefined"!=typeof Map,q="undefined"!=typeof Set,B="undefined"!=typeof Proxy&&void 0!==Proxy.revocable&&"undefined"!=typeof Reflect,H=W?Symbol.for("immer-nothing"):((G={})["immer-nothing"]=!0,G),L=W?Symbol.for("immer-draftable"):"__$immer_draftable",Q=W?Symbol.for("immer-state"):"__$immer_state",Y={0:"Illegal state",1:"Immer drafts cannot have computed properties",2:"This object has been frozen and should not be mutated",3:function(n){return "Cannot use a proxy that has been revoked. Did you pass an object from inside an immer function to an async process? "+n},4:"An immer producer returned a new value *and* modified its draft. Either return a new value *or* modify the draft.",5:"Immer forbids circular references",6:"The first or second argument to `produce` must be a function",7:"The third argument to `produce` must be a function or undefined",8:"First argument to `createDraft` must be a plain object, an array, or an immerable object",9:"First argument to `finishDraft` must be a draft returned by `createDraft`",10:"The given draft is already finalized",11:"Object.defineProperty() cannot be used on an Immer draft",12:"Object.setPrototypeOf() cannot be used on an Immer draft",13:"Immer only supports deleting array indices",14:"Immer only supports setting array indices and the 'length' property",15:function(n){return "Cannot apply patch, path doesn't resolve: "+n},16:'Sets cannot have "replace" patches.',17:function(n){return "Unsupported patch operation: "+n},18:function(n){return "The plugin for '"+n+"' has not been loaded into Immer. To enable the plugin, import and call `enable"+n+"()` when initializing your application."},20:"Cannot use proxies if Proxy, Proxy.revocable or Reflect are not available",21:function(n){return "produce can only be called on things that are draftable: plain objects, arrays, Map, Set or classes that are marked with '[immerable]: true'. Got '"+n+"'"},22:function(n){return "'current' expects a draft, got: "+n},23:function(n){return "'original' expects a draft, got: "+n},24:"Patching reserved attributes like __proto__, prototype and constructor is not allowed"},Z=""+Object.prototype.constructor,nn="undefined"!=typeof Reflect&&Reflect.ownKeys?Reflect.ownKeys:void 0!==Object.getOwnPropertySymbols?function(n){return Object.getOwnPropertyNames(n).concat(Object.getOwnPropertySymbols(n))}:Object.getOwnPropertyNames,rn=Object.getOwnPropertyDescriptors||function(n){var r={};return nn(n).forEach((function(t){r[t]=Object.getOwnPropertyDescriptor(n,t);})),r},tn={},en={get:function(n,r){if(r===Q)return n;var e=p(n);if(!u(e,r))return function(n,r,t){var e,i=I(r,t);return i?"value"in i?i.value:null===(e=i.get)||void 0===e?void 0:e.call(n.k):void 0}(n,e,r);var i=e[r];return n.I||!t(i)?i:i===z(n.t,r)?(E(n),n.o[r]=R(n.A.h,i,n)):i},has:function(n,r){return r in p(n)},ownKeys:function(n){return Reflect.ownKeys(p(n))},set:function(n,r,t){var e=I(p(n),r);if(null==e?void 0:e.set)return e.set.call(n.k,t),!0;if(!n.P){var i=z(p(n),r),o=null==i?void 0:i[Q];if(o&&o.t===t)return n.o[r]=t,n.D[r]=!1,!0;if(c(t,i)&&(void 0!==t||u(n.t,r)))return !0;E(n),k(n);}return n.o[r]===t&&"number"!=typeof t&&(void 0!==t||r in n.o)||(n.o[r]=t,n.D[r]=!0,!0)},deleteProperty:function(n,r){return void 0!==z(n.t,r)||r in n.t?(n.D[r]=!1,E(n),k(n)):delete n.D[r],n.o&&delete n.o[r],!0},getOwnPropertyDescriptor:function(n,r){var t=p(n),e=Reflect.getOwnPropertyDescriptor(t,r);return e?{writable:!0,configurable:1!==n.i||"length"!==r,enumerable:e.enumerable,value:t[r]}:e},defineProperty:function(){n(11);},getPrototypeOf:function(n){return Object.getPrototypeOf(n.t)},setPrototypeOf:function(){n(12);}},on={};i(en,(function(n,r){on[n]=function(){return arguments[0]=arguments[0][0],r.apply(this,arguments)};})),on.deleteProperty=function(r,t){return isNaN(parseInt(t))&&n(13),on.set.call(this,r,t,void 0)},on.set=function(r,t,e){return "length"!==t&&isNaN(parseInt(t))&&n(14),en.set.call(this,r[0],t,e,r[0])};var un=function(){function e(r){var e=this;this.g=B,this.F=!0,this.produce=function(r,i,o){if("function"==typeof r&&"function"!=typeof i){var u=i;i=r;var a=e;return function(n){var r=this;void 0===n&&(n=u);for(var t=arguments.length,e=Array(t>1?t-1:0),o=1;o<t;o++)e[o-1]=arguments[o];return a.produce(n,(function(n){var t;return (t=i).call.apply(t,[r,n].concat(e))}))}}var f;if("function"!=typeof i&&n(6),void 0!==o&&"function"!=typeof o&&n(7),t(r)){var c=w(e),s=R(e,r,void 0),v=!0;try{f=i(s),v=!1;}finally{v?O(c):g(c);}return "undefined"!=typeof Promise&&f instanceof Promise?f.then((function(n){return j(c,o),P(n,c)}),(function(n){throw O(c),n})):(j(c,o),P(f,c))}if(!r||"object"!=typeof r){if(void 0===(f=i(r))&&(f=r),f===H&&(f=void 0),e.F&&d(f,!0),o){var p=[],l=[];b("Patches").M(r,f,p,l),o(p,l);}return f}n(21,r);},this.produceWithPatches=function(n,r){if("function"==typeof n)return function(r){for(var t=arguments.length,i=Array(t>1?t-1:0),o=1;o<t;o++)i[o-1]=arguments[o];return e.produceWithPatches(r,(function(r){return n.apply(void 0,[r].concat(i))}))};var t,i,o=e.produce(n,r,(function(n,r){t=n,i=r;}));return "undefined"!=typeof Promise&&o instanceof Promise?o.then((function(n){return [n,t,i]})):[o,t,i]},"boolean"==typeof(null==r?void 0:r.useProxies)&&this.setUseProxies(r.useProxies),"boolean"==typeof(null==r?void 0:r.autoFreeze)&&this.setAutoFreeze(r.autoFreeze);}var i=e.prototype;return i.createDraft=function(e){t(e)||n(8),r(e)&&(e=D(e));var i=w(this),o=R(this,e,void 0);return o[Q].C=!0,g(i),o},i.finishDraft=function(r,t){var e=r&&r[Q];(e&&e.C||n(9),e.I&&n(10));var i=e.A;return j(i,t),P(void 0,i)},i.setAutoFreeze=function(n){this.F=n;},i.setUseProxies=function(r){r&&!B&&n(20),this.g=r;},i.applyPatches=function(n,t){var e;for(e=t.length-1;e>=0;e--){var i=t[e];if(0===i.path.length&&"replace"===i.op){n=i.value;break}}e>-1&&(t=t.slice(e+1));var o=b("Patches").$;return r(n)?o(n,t):this.produce(n,(function(n){return o(n,t)}))},e}(),an=new un,fn=an.produce;an.produceWithPatches.bind(an);var sn=an.setAutoFreeze.bind(an);an.setUseProxies.bind(an);an.applyPatches.bind(an);an.createDraft.bind(an);an.finishDraft.bind(an);
704
63
 
705
- var history = {
706
- get action() {
707
- return action;
708
- },
64
+ /**
65
+ * router-core
66
+ *
67
+ * Copyright (c) TanStack
68
+ *
69
+ * This source code is licensed under the MIT license found in the
70
+ * LICENSE.md file in the root directory of this source tree.
71
+ *
72
+ * @license MIT
73
+ */
709
74
 
75
+ // While the public API was clearly inspired by the "history" npm package,
76
+ // This implementation attempts to be more lightweight by
77
+ // making assumptions about the way TanStack Router works
78
+
79
+ const popStateEvent = 'popstate';
80
+ function createHistory(opts) {
81
+ let currentLocation = opts.getLocation();
82
+ let unsub = () => {};
83
+ let listeners = new Set();
84
+ const onUpdate = () => {
85
+ currentLocation = opts.getLocation();
86
+ listeners.forEach(listener => listener());
87
+ };
88
+ return {
710
89
  get location() {
711
- return location;
712
- },
713
-
714
- createHref: createHref,
715
- push: push,
716
- replace: replace,
717
- go: go,
718
- back: function back() {
719
- go(-1);
720
- },
721
- forward: function forward() {
722
- go(1);
723
- },
724
- listen: function listen(listener) {
725
- return listeners.push(listener);
90
+ return currentLocation;
726
91
  },
727
- block: function block(blocker) {
728
- var unblock = blockers.push(blocker);
729
-
730
- if (blockers.length === 1) {
731
- window.addEventListener(BeforeUnloadEventType, promptBeforeUnload);
92
+ listen: cb => {
93
+ if (listeners.size === 0) {
94
+ unsub = opts.listener(onUpdate);
732
95
  }
733
-
734
- return function () {
735
- unblock(); // Remove the beforeunload listener so the document may
736
- // still be salvageable in the pagehide event.
737
- // See https://html.spec.whatwg.org/#unloading-documents
738
-
739
- if (!blockers.length) {
740
- window.removeEventListener(BeforeUnloadEventType, promptBeforeUnload);
96
+ listeners.add(cb);
97
+ return () => {
98
+ listeners.delete(cb);
99
+ if (listeners.size === 0) {
100
+ unsub();
741
101
  }
742
102
  };
743
- }
744
- };
745
- return history;
746
- }
747
- /**
748
- * Hash history stores the location in window.location.hash. This makes it ideal
749
- * for situations where you don't want to send the location to the server for
750
- * some reason, either because you do cannot configure it or the URL space is
751
- * reserved for something else.
752
- *
753
- * @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#createhashhistory
754
- */
755
-
756
- function createHashHistory(options) {
757
- if (options === void 0) {
758
- options = {};
759
- }
760
-
761
- var _options2 = options,
762
- _options2$window = _options2.window,
763
- window = _options2$window === void 0 ? document.defaultView : _options2$window;
764
- var globalHistory = window.history;
765
-
766
- function getIndexAndLocation() {
767
- var _parsePath = parsePath(window.location.hash.substr(1)),
768
- _parsePath$pathname = _parsePath.pathname,
769
- pathname = _parsePath$pathname === void 0 ? '/' : _parsePath$pathname,
770
- _parsePath$search = _parsePath.search,
771
- search = _parsePath$search === void 0 ? '' : _parsePath$search,
772
- _parsePath$hash = _parsePath.hash,
773
- hash = _parsePath$hash === void 0 ? '' : _parsePath$hash;
774
-
775
- var state = globalHistory.state || {};
776
- return [state.idx, readOnly({
777
- pathname: pathname,
778
- search: search,
779
- hash: hash,
780
- state: state.usr || null,
781
- key: state.key || 'default'
782
- })];
783
- }
784
-
785
- var blockedPopTx = null;
786
-
787
- function handlePop() {
788
- if (blockedPopTx) {
789
- blockers.call(blockedPopTx);
790
- blockedPopTx = null;
791
- } else {
792
- var nextAction = Action.Pop;
793
-
794
- var _getIndexAndLocation4 = getIndexAndLocation(),
795
- nextIndex = _getIndexAndLocation4[0],
796
- nextLocation = _getIndexAndLocation4[1];
797
-
798
- if (blockers.length) {
799
- if (nextIndex != null) {
800
- var delta = index - nextIndex;
801
-
802
- if (delta) {
803
- // Revert the POP
804
- blockedPopTx = {
805
- action: nextAction,
806
- location: nextLocation,
807
- retry: function retry() {
808
- go(delta * -1);
809
- }
810
- };
811
- go(delta);
812
- }
813
- } else {
814
- // Trying to POP to a location with no index. We did not create
815
- // this location, so we can't effectively block the navigation.
816
- warning$1(false, // TODO: Write up a doc that explains our blocking strategy in
817
- // detail and link to it here so people can understand better
818
- // what is going on and how to avoid it.
819
- "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.") ;
820
- }
821
- } else {
822
- applyTx(nextAction);
823
- }
824
- }
825
- }
826
-
827
- window.addEventListener(PopStateEventType, handlePop); // popstate does not fire on hashchange in IE 11 and old (trident) Edge
828
- // https://developer.mozilla.org/de/docs/Web/API/Window/popstate_event
829
-
830
- window.addEventListener(HashChangeEventType, function () {
831
- var _getIndexAndLocation5 = getIndexAndLocation(),
832
- nextLocation = _getIndexAndLocation5[1]; // Ignore extraneous hashchange events.
833
-
834
-
835
- if (createPath(nextLocation) !== createPath(location)) {
836
- handlePop();
837
- }
838
- });
839
- var action = Action.Pop;
840
-
841
- var _getIndexAndLocation6 = getIndexAndLocation(),
842
- index = _getIndexAndLocation6[0],
843
- location = _getIndexAndLocation6[1];
844
-
845
- var listeners = createEvents();
846
- var blockers = createEvents();
847
-
848
- if (index == null) {
849
- index = 0;
850
- globalHistory.replaceState(_extends({}, globalHistory.state, {
851
- idx: index
852
- }), '');
853
- }
854
-
855
- function getBaseHref() {
856
- var base = document.querySelector('base');
857
- var href = '';
858
-
859
- if (base && base.getAttribute('href')) {
860
- var url = window.location.href;
861
- var hashIndex = url.indexOf('#');
862
- href = hashIndex === -1 ? url : url.slice(0, hashIndex);
863
- }
864
-
865
- return href;
866
- }
867
-
868
- function createHref(to) {
869
- return getBaseHref() + '#' + (typeof to === 'string' ? to : createPath(to));
870
- }
871
-
872
- function getNextLocation(to, state) {
873
- if (state === void 0) {
874
- state = null;
875
- }
876
-
877
- return readOnly(_extends({
878
- pathname: location.pathname,
879
- hash: '',
880
- search: ''
881
- }, typeof to === 'string' ? parsePath(to) : to, {
882
- state: state,
883
- key: createKey()
884
- }));
885
- }
886
-
887
- function getHistoryStateAndUrl(nextLocation, index) {
888
- return [{
889
- usr: nextLocation.state,
890
- key: nextLocation.key,
891
- idx: index
892
- }, createHref(nextLocation)];
893
- }
894
-
895
- function allowTx(action, location, retry) {
896
- return !blockers.length || (blockers.call({
897
- action: action,
898
- location: location,
899
- retry: retry
900
- }), false);
901
- }
902
-
903
- function applyTx(nextAction) {
904
- action = nextAction;
905
-
906
- var _getIndexAndLocation7 = getIndexAndLocation();
907
-
908
- index = _getIndexAndLocation7[0];
909
- location = _getIndexAndLocation7[1];
910
- listeners.call({
911
- action: action,
912
- location: location
913
- });
914
- }
915
-
916
- function push(to, state) {
917
- var nextAction = Action.Push;
918
- var nextLocation = getNextLocation(to, state);
919
-
920
- function retry() {
921
- push(to, state);
922
- }
923
-
924
- warning$1(nextLocation.pathname.charAt(0) === '/', "Relative pathnames are not supported in hash history.push(" + JSON.stringify(to) + ")") ;
925
-
926
- if (allowTx(nextAction, nextLocation, retry)) {
927
- var _getHistoryStateAndUr3 = getHistoryStateAndUrl(nextLocation, index + 1),
928
- historyState = _getHistoryStateAndUr3[0],
929
- url = _getHistoryStateAndUr3[1]; // TODO: Support forced reloading
930
- // try...catch because iOS limits us to 100 pushState calls :/
931
-
932
-
933
- try {
934
- globalHistory.pushState(historyState, '', url);
935
- } catch (error) {
936
- // They are going to lose state here, but there is no real
937
- // way to warn them about it since the page will refresh...
938
- window.location.assign(url);
939
- }
940
-
941
- applyTx(nextAction);
942
- }
943
- }
944
-
945
- function replace(to, state) {
946
- var nextAction = Action.Replace;
947
- var nextLocation = getNextLocation(to, state);
948
-
949
- function retry() {
950
- replace(to, state);
951
- }
952
-
953
- warning$1(nextLocation.pathname.charAt(0) === '/', "Relative pathnames are not supported in hash history.replace(" + JSON.stringify(to) + ")") ;
954
-
955
- if (allowTx(nextAction, nextLocation, retry)) {
956
- var _getHistoryStateAndUr4 = getHistoryStateAndUrl(nextLocation, index),
957
- historyState = _getHistoryStateAndUr4[0],
958
- url = _getHistoryStateAndUr4[1]; // TODO: Support forced reloading
959
-
960
-
961
- globalHistory.replaceState(historyState, '', url);
962
- applyTx(nextAction);
963
- }
964
- }
965
-
966
- function go(delta) {
967
- globalHistory.go(delta);
968
- }
969
-
970
- var history = {
971
- get action() {
972
- return action;
973
103
  },
974
-
975
- get location() {
976
- return location;
104
+ push: (path, state) => {
105
+ opts.pushState(path, state);
106
+ onUpdate();
977
107
  },
978
-
979
- createHref: createHref,
980
- push: push,
981
- replace: replace,
982
- go: go,
983
- back: function back() {
984
- go(-1);
108
+ replace: (path, state) => {
109
+ opts.replaceState(path, state);
110
+ onUpdate();
985
111
  },
986
- forward: function forward() {
987
- go(1);
112
+ go: index => {
113
+ opts.go(index);
114
+ onUpdate();
988
115
  },
989
- listen: function listen(listener) {
990
- return listeners.push(listener);
116
+ back: () => {
117
+ opts.back();
118
+ onUpdate();
991
119
  },
992
- block: function block(blocker) {
993
- var unblock = blockers.push(blocker);
994
-
995
- if (blockers.length === 1) {
996
- window.addEventListener(BeforeUnloadEventType, promptBeforeUnload);
997
- }
998
-
999
- return function () {
1000
- unblock(); // Remove the beforeunload listener so the document may
1001
- // still be salvageable in the pagehide event.
1002
- // See https://html.spec.whatwg.org/#unloading-documents
1003
-
1004
- if (!blockers.length) {
1005
- window.removeEventListener(BeforeUnloadEventType, promptBeforeUnload);
1006
- }
1007
- };
120
+ forward: () => {
121
+ opts.forward();
122
+ onUpdate();
1008
123
  }
1009
124
  };
1010
- return history;
1011
125
  }
1012
- /**
1013
- * Memory history stores the current location in memory. It is designed for use
1014
- * in stateful non-browser environments like tests and React Native.
1015
- *
1016
- * @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#creatememoryhistory
1017
- */
1018
-
1019
- function createMemoryHistory(options) {
1020
- if (options === void 0) {
1021
- options = {};
1022
- }
1023
-
1024
- var _options3 = options,
1025
- _options3$initialEntr = _options3.initialEntries,
1026
- initialEntries = _options3$initialEntr === void 0 ? ['/'] : _options3$initialEntr,
1027
- initialIndex = _options3.initialIndex;
1028
- var entries = initialEntries.map(function (entry) {
1029
- var location = readOnly(_extends({
1030
- pathname: '/',
1031
- search: '',
1032
- hash: '',
1033
- state: null,
1034
- key: createKey()
1035
- }, typeof entry === 'string' ? parsePath(entry) : entry));
1036
- warning$1(location.pathname.charAt(0) === '/', "Relative pathnames are not supported in createMemoryHistory({ initialEntries }) (invalid entry: " + JSON.stringify(entry) + ")") ;
1037
- return location;
1038
- });
1039
- var index = clamp(initialIndex == null ? entries.length - 1 : initialIndex, 0, entries.length - 1);
1040
- var action = Action.Pop;
1041
- var location = entries[index];
1042
- var listeners = createEvents();
1043
- var blockers = createEvents();
1044
-
1045
- function createHref(to) {
1046
- return typeof to === 'string' ? to : createPath(to);
1047
- }
1048
-
1049
- function getNextLocation(to, state) {
1050
- if (state === void 0) {
1051
- state = null;
1052
- }
1053
-
1054
- return readOnly(_extends({
1055
- pathname: location.pathname,
1056
- search: '',
1057
- hash: ''
1058
- }, typeof to === 'string' ? parsePath(to) : to, {
1059
- state: state,
1060
- key: createKey()
1061
- }));
1062
- }
1063
-
1064
- function allowTx(action, location, retry) {
1065
- return !blockers.length || (blockers.call({
1066
- action: action,
1067
- location: location,
1068
- retry: retry
1069
- }), false);
1070
- }
1071
-
1072
- function applyTx(nextAction, nextLocation) {
1073
- action = nextAction;
1074
- location = nextLocation;
1075
- listeners.call({
1076
- action: action,
1077
- location: location
1078
- });
1079
- }
1080
-
1081
- function push(to, state) {
1082
- var nextAction = Action.Push;
1083
- var nextLocation = getNextLocation(to, state);
1084
-
1085
- function retry() {
1086
- push(to, state);
1087
- }
1088
-
1089
- warning$1(location.pathname.charAt(0) === '/', "Relative pathnames are not supported in memory history.push(" + JSON.stringify(to) + ")") ;
1090
-
1091
- if (allowTx(nextAction, nextLocation, retry)) {
1092
- index += 1;
1093
- entries.splice(index, entries.length, nextLocation);
1094
- applyTx(nextAction, nextLocation);
1095
- }
1096
- }
1097
-
1098
- function replace(to, state) {
1099
- var nextAction = Action.Replace;
1100
- var nextLocation = getNextLocation(to, state);
1101
-
1102
- function retry() {
1103
- replace(to, state);
1104
- }
1105
-
1106
- warning$1(location.pathname.charAt(0) === '/', "Relative pathnames are not supported in memory history.replace(" + JSON.stringify(to) + ")") ;
1107
-
1108
- if (allowTx(nextAction, nextLocation, retry)) {
1109
- entries[index] = nextLocation;
1110
- applyTx(nextAction, nextLocation);
1111
- }
1112
- }
1113
-
1114
- function go(delta) {
1115
- var nextIndex = clamp(index + delta, 0, entries.length - 1);
1116
- var nextAction = Action.Pop;
1117
- var nextLocation = entries[nextIndex];
1118
-
1119
- function retry() {
1120
- go(delta);
1121
- }
1122
-
1123
- if (allowTx(nextAction, nextLocation, retry)) {
1124
- index = nextIndex;
1125
- applyTx(nextAction, nextLocation);
1126
- }
1127
- }
1128
-
1129
- var history = {
1130
- get index() {
1131
- return index;
1132
- },
1133
-
1134
- get action() {
1135
- return action;
1136
- },
1137
-
1138
- get location() {
1139
- return location;
1140
- },
1141
-
1142
- createHref: createHref,
1143
- push: push,
1144
- replace: replace,
1145
- go: go,
1146
- back: function back() {
1147
- go(-1);
126
+ function createBrowserHistory(opts) {
127
+ const getHref = opts?.getHref ?? (() => `${window.location.pathname}${window.location.hash}${window.location.search}`);
128
+ const createHref = opts?.createHref ?? (path => path);
129
+ const getLocation = () => parseLocation(getHref(), history.state);
130
+ return createHistory({
131
+ getLocation,
132
+ listener: onUpdate => {
133
+ window.addEventListener(popStateEvent, onUpdate);
134
+ return () => {
135
+ window.removeEventListener(popStateEvent, onUpdate);
136
+ };
1148
137
  },
1149
- forward: function forward() {
1150
- go(1);
138
+ pushState: (path, state) => {
139
+ window.history.pushState({
140
+ ...state,
141
+ key: createRandomKey()
142
+ }, '', createHref(path));
1151
143
  },
1152
- listen: function listen(listener) {
1153
- return listeners.push(listener);
144
+ replaceState: (path, state) => {
145
+ window.history.replaceState({
146
+ ...state,
147
+ key: createRandomKey()
148
+ }, '', createHref(path));
1154
149
  },
1155
- block: function block(blocker) {
1156
- return blockers.push(blocker);
1157
- }
1158
- };
1159
- return history;
1160
- } ////////////////////////////////////////////////////////////////////////////////
1161
- // UTILS
1162
- ////////////////////////////////////////////////////////////////////////////////
1163
-
1164
- function clamp(n, lowerBound, upperBound) {
1165
- return Math.min(Math.max(n, lowerBound), upperBound);
150
+ back: () => window.history.back(),
151
+ forward: () => window.history.forward(),
152
+ go: n => window.history.go(n)
153
+ });
1166
154
  }
1167
-
1168
- function promptBeforeUnload(event) {
1169
- // Cancel the event.
1170
- event.preventDefault(); // Chrome (and legacy IE) requires returnValue to be set.
1171
-
1172
- event.returnValue = '';
155
+ function createHashHistory() {
156
+ return createBrowserHistory({
157
+ getHref: () => window.location.hash.substring(1),
158
+ createHref: path => `#${path}`
159
+ });
1173
160
  }
1174
-
1175
- function createEvents() {
1176
- var handlers = [];
1177
- return {
1178
- get length() {
1179
- return handlers.length;
161
+ function createMemoryHistory(opts = {
162
+ initialEntries: ['/']
163
+ }) {
164
+ const entries = opts.initialEntries;
165
+ let index = opts.initialIndex ?? entries.length - 1;
166
+ let currentState = {};
167
+ const getLocation = () => parseLocation(entries[index], currentState);
168
+ return createHistory({
169
+ getLocation,
170
+ listener: onUpdate => {
171
+ window.addEventListener(popStateEvent, onUpdate);
172
+ // We might need to handle the hashchange event in the future
173
+ // window.addEventListener(hashChangeEvent, onUpdate)
174
+ return () => {
175
+ window.removeEventListener(popStateEvent, onUpdate);
176
+ };
1180
177
  },
1181
-
1182
- push: function push(fn) {
1183
- handlers.push(fn);
1184
- return function () {
1185
- handlers = handlers.filter(function (handler) {
1186
- return handler !== fn;
1187
- });
178
+ pushState: (path, state) => {
179
+ currentState = {
180
+ ...state,
181
+ key: createRandomKey()
1188
182
  };
183
+ entries.push(path);
184
+ index++;
1189
185
  },
1190
- call: function call(arg) {
1191
- handlers.forEach(function (fn) {
1192
- return fn && fn(arg);
1193
- });
1194
- }
1195
- };
1196
- }
1197
-
1198
- function createKey() {
1199
- return Math.random().toString(36).substr(2, 8);
186
+ replaceState: (path, state) => {
187
+ currentState = {
188
+ ...state,
189
+ key: createRandomKey()
190
+ };
191
+ entries[index] = path;
192
+ },
193
+ back: () => {
194
+ index--;
195
+ },
196
+ forward: () => {
197
+ index = Math.min(index + 1, entries.length - 1);
198
+ },
199
+ go: n => window.history.go(n)
200
+ });
1200
201
  }
1201
- /**
1202
- * Creates a string URL path from the given pathname, search, and hash components.
1203
- *
1204
- * @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#createpath
1205
- */
1206
-
1207
-
1208
- function createPath(_ref) {
1209
- var _ref$pathname = _ref.pathname,
1210
- pathname = _ref$pathname === void 0 ? '/' : _ref$pathname,
1211
- _ref$search = _ref.search,
1212
- search = _ref$search === void 0 ? '' : _ref$search,
1213
- _ref$hash = _ref.hash,
1214
- hash = _ref$hash === void 0 ? '' : _ref$hash;
1215
- if (search && search !== '?') pathname += search.charAt(0) === '?' ? search : '?' + search;
1216
- if (hash && hash !== '#') pathname += hash.charAt(0) === '#' ? hash : '#' + hash;
1217
- return pathname;
1218
- }
1219
- /**
1220
- * Parses a string URL path into its separate pathname, search, and hash components.
1221
- *
1222
- * @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#parsepath
1223
- */
1224
-
1225
- function parsePath(path) {
1226
- var parsedPath = {};
1227
-
1228
- if (path) {
1229
- var hashIndex = path.indexOf('#');
1230
-
1231
- if (hashIndex >= 0) {
1232
- parsedPath.hash = path.substr(hashIndex);
1233
- path = path.substr(0, hashIndex);
1234
- }
1235
-
1236
- var searchIndex = path.indexOf('?');
1237
-
1238
- if (searchIndex >= 0) {
1239
- parsedPath.search = path.substr(searchIndex);
1240
- path = path.substr(0, searchIndex);
1241
- }
1242
-
1243
- if (path) {
1244
- parsedPath.pathname = path;
1245
- }
1246
- }
1247
-
1248
- return parsedPath;
202
+ function parseLocation(href, state) {
203
+ let hashIndex = href.indexOf('#');
204
+ let searchIndex = href.indexOf('?');
205
+ const pathEnd = Math.min(hashIndex, searchIndex);
206
+ return {
207
+ href,
208
+ pathname: pathEnd > -1 ? href.substring(0, pathEnd) : href,
209
+ hash: hashIndex > -1 ? href.substring(hashIndex, searchIndex) : '',
210
+ search: searchIndex > -1 ? href.substring(searchIndex) : '',
211
+ state
212
+ };
1249
213
  }
1250
214
 
1251
- var prefix = 'Invariant failed';
1252
- function invariant(condition, message) {
1253
- if (condition) {
1254
- return;
1255
- }
1256
- var provided = typeof message === 'function' ? message() : message;
1257
- var value = provided ? "".concat(prefix, ": ").concat(provided) : prefix;
1258
- throw new Error(value);
215
+ // Thanks co-pilot!
216
+ function createRandomKey() {
217
+ return (Math.random() + 1).toString(36).substring(7);
1259
218
  }
1260
219
 
1261
- /**
1262
- * router-core
1263
- *
1264
- * Copyright (c) TanStack
1265
- *
1266
- * This source code is licensed under the MIT license found in the
1267
- * LICENSE.md file in the root directory of this source tree.
1268
- *
1269
- * @license MIT
1270
- */
1271
-
1272
220
  function last(arr) {
1273
221
  return arr[arr.length - 1];
1274
222
  }
@@ -1328,9 +276,8 @@
1328
276
  baseSegments.push(toSegment);
1329
277
  } else ;
1330
278
  } else if (toSegment.value === '..') {
1331
- var _last;
1332
279
  // Extra trailing slash? pop it off
1333
- if (baseSegments.length > 1 && ((_last = last(baseSegments)) == null ? void 0 : _last.value) === '/') {
280
+ if (baseSegments.length > 1 && last(baseSegments)?.value === '/') {
1334
281
  baseSegments.pop();
1335
282
  }
1336
283
  baseSegments.pop();
@@ -1427,14 +374,14 @@
1427
374
  const isLastBaseSegment = i === baseSegments.length - 1;
1428
375
  if (routeSegment) {
1429
376
  if (routeSegment.type === 'wildcard') {
1430
- if (baseSegment != null && baseSegment.value) {
377
+ if (baseSegment?.value) {
1431
378
  params['*'] = joinPaths(baseSegments.slice(i).map(d => d.value));
1432
379
  return true;
1433
380
  }
1434
381
  return false;
1435
382
  }
1436
383
  if (routeSegment.type === 'pathname') {
1437
- if (routeSegment.value === '/' && !(baseSegment != null && baseSegment.value)) {
384
+ if (routeSegment.value === '/' && !baseSegment?.value) {
1438
385
  return true;
1439
386
  }
1440
387
  if (baseSegment) {
@@ -1451,7 +398,7 @@
1451
398
  return false;
1452
399
  }
1453
400
  if (routeSegment.type === 'param') {
1454
- if ((baseSegment == null ? void 0 : baseSegment.value) === '/') {
401
+ if (baseSegment?.value === '/') {
1455
402
  return false;
1456
403
  }
1457
404
  if (baseSegment.value.charAt(0) !== '$') {
@@ -1517,151 +464,25 @@
1517
464
  return out;
1518
465
  }
1519
466
 
1520
- function createRoute(routeConfig, options, originalIndex, parent, router) {
1521
- const {
1522
- id,
1523
- routeId,
1524
- path: routePath,
1525
- fullPath
1526
- } = routeConfig;
1527
- let route = {
1528
- routeInfo: undefined,
1529
- routeId: id,
1530
- routeRouteId: routeId,
1531
- originalIndex,
1532
- routePath,
1533
- fullPath,
1534
- options,
1535
- router,
1536
- childRoutes: undefined,
1537
- parentRoute: parent,
1538
- get action() {
1539
- let action = router.store.actions[id] || (() => {
1540
- router.setStore(s => {
1541
- s.actions[id] = {
1542
- submissions: [],
1543
- submit: async (submission, actionOpts) => {
1544
- if (!route) {
1545
- return;
1546
- }
1547
- const invalidate = (actionOpts == null ? void 0 : actionOpts.invalidate) ?? true;
1548
- const [actionStore, setActionStore] = createStore({
1549
- submittedAt: Date.now(),
1550
- status: 'pending',
1551
- submission,
1552
- isMulti: !!(actionOpts != null && actionOpts.multi)
1553
- });
1554
- router.setStore(s => {
1555
- if (!(actionOpts != null && actionOpts.multi)) {
1556
- s.actions[id].submissions = action.submissions.filter(d => d.isMulti);
1557
- }
1558
- s.actions[id].current = actionStore;
1559
- s.actions[id].latest = actionStore;
1560
- s.actions[id].submissions.push(actionStore);
1561
- });
1562
- try {
1563
- const res = await (route.options.action == null ? void 0 : route.options.action(submission));
1564
- setActionStore(s => {
1565
- s.data = res;
1566
- });
1567
- if (invalidate) {
1568
- router.invalidateRoute({
1569
- to: '.',
1570
- fromCurrent: true
1571
- });
1572
- await router.reload();
1573
- }
1574
- setActionStore(s => {
1575
- s.status = 'success';
1576
- });
1577
- return res;
1578
- } catch (err) {
1579
- console.error(err);
1580
- setActionStore(s => {
1581
- s.error = err;
1582
- s.status = 'error';
1583
- });
1584
- }
1585
- }
1586
- };
1587
- });
1588
- return router.store.actions[id];
1589
- })();
1590
- return action;
1591
- },
1592
- get loader() {
1593
- let loader = router.store.loaders[id] || (() => {
1594
- router.setStore(s => {
1595
- s.loaders[id] = {
1596
- pending: [],
1597
- fetch: async loaderContext => {
1598
- if (!route) {
1599
- return;
1600
- }
1601
- const loaderState = {
1602
- loadedAt: Date.now(),
1603
- loaderContext
1604
- };
1605
- router.setStore(s => {
1606
- s.loaders[id].current = loaderState;
1607
- s.loaders[id].latest = loaderState;
1608
- s.loaders[id].pending.push(loaderState);
1609
- });
1610
- try {
1611
- return await (route.options.loader == null ? void 0 : route.options.loader(loaderContext));
1612
- } finally {
1613
- router.setStore(s => {
1614
- s.loaders[id].pending = s.loaders[id].pending.filter(d => d !== loaderState);
1615
- });
1616
- }
1617
- }
1618
- };
1619
- });
1620
- return router.store.loaders[id];
1621
- })();
1622
- return loader;
1623
- }
1624
-
1625
- // buildLink: (options) => {
1626
- // return router.buildLink({
1627
- // ...options,
1628
- // from: fullPath,
1629
- // } as any) as any
1630
- // },
1631
-
1632
- // navigate: (options) => {
1633
- // return router.navigate({
1634
- // ...options,
1635
- // from: fullPath,
1636
- // } as any) as any
1637
- // },
1638
-
1639
- // matchRoute: (matchLocation, opts) => {
1640
- // return router.matchRoute(
1641
- // {
1642
- // ...matchLocation,
1643
- // from: fullPath,
1644
- // } as any,
1645
- // opts,
1646
- // ) as any
1647
- // },
1648
- };
1649
-
1650
- router.options.createRoute == null ? void 0 : router.options.createRoute({
1651
- router,
1652
- route
1653
- });
1654
- return route;
467
+ class Route {
468
+ constructor(routeConfig, options, originalIndex, parent, router) {
469
+ Object.assign(this, {
470
+ ...routeConfig,
471
+ originalIndex,
472
+ options,
473
+ getRouter: () => router,
474
+ childRoutes: undefined,
475
+ getParentRoute: () => parent
476
+ });
477
+ router.options.createRoute?.({
478
+ router,
479
+ route: this
480
+ });
481
+ }
1655
482
  }
1656
483
 
1657
484
  const rootRouteId = '__root__';
1658
- const createRouteConfig = function (options, children, isRoot, parentId, parentPath) {
1659
- if (options === void 0) {
1660
- options = {};
1661
- }
1662
- if (isRoot === void 0) {
1663
- isRoot = true;
1664
- }
485
+ const createRouteConfig = (options = {}, children = [], isRoot = true, parentId, parentPath) => {
1665
486
  if (isRoot) {
1666
487
  options.path = rootRouteId;
1667
488
  }
@@ -1700,80 +521,153 @@
1700
521
  };
1701
522
  };
1702
523
 
524
+ sn(false);
525
+ let queue = [];
526
+ let batching = false;
527
+ function flush() {
528
+ if (batching) return;
529
+ queue.forEach(cb => cb());
530
+ queue = [];
531
+ }
532
+ function createStore(initialState, debug) {
533
+ const listeners = new Set();
534
+ const store = {
535
+ state: initialState,
536
+ subscribe: listener => {
537
+ listeners.add(listener);
538
+ return () => listeners.delete(listener);
539
+ },
540
+ setState: updater => {
541
+ const previous = store.state;
542
+ store.state = fn(d => {
543
+ updater(d);
544
+ })(previous);
545
+ if (debug) console.log(store.state);
546
+ queue.push(() => listeners.forEach(listener => listener(store.state, previous)));
547
+ flush();
548
+ }
549
+ };
550
+ return store;
551
+ }
552
+ function batch(cb) {
553
+ batching = true;
554
+ cb();
555
+ batching = false;
556
+ flush();
557
+ }
558
+
559
+ // /**
560
+ // * This function converts a store to an immutable value, which is
561
+ // * more complex than you think. On first read, (when prev is undefined)
562
+ // * every value must be recursively touched so tracking is "deep".
563
+ // * Every object/array structure must also be cloned to
564
+ // * have a new reference, otherwise it will get mutated by subsequent
565
+ // * store updates.
566
+ // *
567
+ // * In the case that prev is supplied, we have to do deep comparisons
568
+ // * between prev and next objects/array references and if they are deeply
569
+ // * equal, we can return the prev version for referential equality.
570
+ // */
571
+ // export function storeToImmutable<T>(prev: any, next: T): T {
572
+ // const cache = new Map()
573
+
574
+ // // Visit all nodes
575
+ // // clone all next structures
576
+ // // from bottom up, if prev === next, return prev
577
+
578
+ // function recurse(prev: any, next: any) {
579
+ // if (cache.has(next)) {
580
+ // return cache.get(next)
581
+ // }
582
+
583
+ // const prevIsArray = Array.isArray(prev)
584
+ // const nextIsArray = Array.isArray(next)
585
+ // const prevIsObj = isPlainObject(prev)
586
+ // const nextIsObj = isPlainObject(next)
587
+ // const nextIsComplex = nextIsArray || nextIsObj
588
+
589
+ // const isArray = prevIsArray && nextIsArray
590
+ // const isObj = prevIsObj && nextIsObj
591
+
592
+ // const isSameStructure = isArray || isObj
593
+
594
+ // if (nextIsComplex) {
595
+ // const prevSize = isArray
596
+ // ? prev.length
597
+ // : isObj
598
+ // ? Object.keys(prev).length
599
+ // : -1
600
+ // const nextKeys = isArray ? next : Object.keys(next)
601
+ // const nextSize = nextKeys.length
602
+
603
+ // let changed = false
604
+ // const copy: any = nextIsArray ? [] : {}
605
+
606
+ // for (let i = 0; i < nextSize; i++) {
607
+ // const key = isArray ? i : nextKeys[i]
608
+ // const prevValue = isSameStructure ? prev[key] : undefined
609
+ // const nextValue = next[key]
610
+
611
+ // // Recurse the new value
612
+ // try {
613
+ // console.count(key)
614
+ // copy[key] = recurse(prevValue, nextValue)
615
+ // } catch {}
616
+
617
+ // // If the new value has changed reference,
618
+ // // mark the obj/array as changed
619
+ // if (!changed && copy[key] !== prevValue) {
620
+ // changed = true
621
+ // }
622
+ // }
623
+
624
+ // // No items have changed!
625
+ // // If something has changed, return a clone of the next obj/array
626
+ // if (changed || prevSize !== nextSize) {
627
+ // cache.set(next, copy)
628
+ // return copy
629
+ // }
630
+
631
+ // // If they are exactly the same, return the prev obj/array
632
+ // cache.set(next, prev)
633
+ // return prev
634
+ // }
635
+
636
+ // cache.set(next, next)
637
+ // return next
638
+ // }
639
+
640
+ // return recurse(prev, next)
641
+ // }
642
+
1703
643
  /**
1704
644
  * This function returns `a` if `b` is deeply equal.
1705
645
  * If not, it will replace any deeply equal children of `b` with those of `a`.
1706
- * This can be used for structural sharing between JSON values for example.
646
+ * This can be used for structural sharing between immutable JSON values for example.
647
+ * Do not use this with signals
1707
648
  */
1708
- function sharedClone(prev, next, touchAll) {
1709
- const things = new Map();
1710
- function recurse(prev, next) {
1711
- if (prev === next) {
1712
- return prev;
1713
- }
1714
- if (things.has(next)) {
1715
- return things.get(next);
1716
- }
1717
- const prevIsArray = Array.isArray(prev);
1718
- const nextIsArray = Array.isArray(next);
1719
- const prevIsObj = isPlainObject(prev);
1720
- const nextIsObj = isPlainObject(next);
1721
- const isArray = prevIsArray && nextIsArray;
1722
- const isObj = prevIsObj && nextIsObj;
1723
- const isSameStructure = isArray || isObj;
1724
-
1725
- // Both are arrays or objects
1726
- if (isSameStructure) {
1727
- const aSize = isArray ? prev.length : Object.keys(prev).length;
1728
- const bItems = isArray ? next : Object.keys(next);
1729
- const bSize = bItems.length;
1730
- const copy = isArray ? [] : {};
1731
- let equalItems = 0;
1732
- for (let i = 0; i < bSize; i++) {
1733
- const key = isArray ? i : bItems[i];
1734
- if (copy[key] === prev[key]) {
1735
- equalItems++;
1736
- }
1737
- }
1738
- if (aSize === bSize && equalItems === aSize) {
1739
- things.set(next, prev);
1740
- return prev;
1741
- }
1742
- things.set(next, copy);
1743
- for (let i = 0; i < bSize; i++) {
1744
- const key = isArray ? i : bItems[i];
1745
- if (typeof bItems[i] === 'function') {
1746
- copy[key] = prev[key];
1747
- } else {
1748
- copy[key] = recurse(prev[key], next[key]);
1749
- }
1750
- if (copy[key] === prev[key]) {
1751
- equalItems++;
1752
- }
1753
- }
1754
- return copy;
1755
- }
1756
- if (nextIsArray) {
1757
- const copy = [];
1758
- things.set(next, copy);
1759
- for (let i = 0; i < next.length; i++) {
1760
- copy[i] = recurse(undefined, next[i]);
649
+ function replaceEqualDeep(prev, _next) {
650
+ if (prev === _next) {
651
+ return prev;
652
+ }
653
+ const next = _next;
654
+ const array = Array.isArray(prev) && Array.isArray(next);
655
+ if (array || isPlainObject(prev) && isPlainObject(next)) {
656
+ const prevSize = array ? prev.length : Object.keys(prev).length;
657
+ const nextItems = array ? next : Object.keys(next);
658
+ const nextSize = nextItems.length;
659
+ const copy = array ? [] : {};
660
+ let equalItems = 0;
661
+ for (let i = 0; i < nextSize; i++) {
662
+ const key = array ? i : nextItems[i];
663
+ copy[key] = replaceEqualDeep(prev[key], next[key]);
664
+ if (copy[key] === prev[key]) {
665
+ equalItems++;
1761
666
  }
1762
- return copy;
1763
667
  }
1764
- if (nextIsObj) {
1765
- const copy = {};
1766
- things.set(next, copy);
1767
- const nextKeys = Object.keys(next);
1768
- for (let i = 0; i < nextKeys.length; i++) {
1769
- const key = nextKeys[i];
1770
- copy[key] = recurse(undefined, next[key]);
1771
- }
1772
- return copy;
1773
- }
1774
- return next;
668
+ return prevSize === nextSize && equalItems === prevSize ? prev : copy;
1775
669
  }
1776
- return recurse(prev, next);
670
+ return next;
1777
671
  }
1778
672
 
1779
673
  // Copied from: https://github.com/jonschlinkert/is-plain-object
@@ -1805,1039 +699,1072 @@
1805
699
  function hasObjectPrototype(o) {
1806
700
  return Object.prototype.toString.call(o) === '[object Object]';
1807
701
  }
702
+ function trackDeep(obj) {
703
+ const seen = new Set();
704
+ JSON.stringify(obj, (_, value) => {
705
+ if (typeof value === 'function') {
706
+ return undefined;
707
+ }
708
+ if (typeof value === 'object' && value !== null) {
709
+ if (seen.has(value)) return;
710
+ seen.add(value);
711
+ }
712
+ return value;
713
+ });
714
+ return obj;
715
+ }
1808
716
 
1809
717
  const componentTypes = ['component', 'errorComponent', 'pendingComponent'];
1810
- function createRouteMatch(router, route, opts) {
1811
- let componentsPromise;
1812
- let dataPromise;
1813
- let latestId = '';
1814
- let resolve = () => {};
1815
- function setLoaderData(loaderData) {
1816
- batch(() => {
1817
- setStore(s => {
1818
- s.routeLoaderData = sharedClone(s.routeLoaderData, loaderData);
1819
- });
1820
- updateLoaderData();
718
+ class RouteMatch {
719
+ abortController = new AbortController();
720
+ #latestId = '';
721
+ #resolve = () => {};
722
+ onLoaderDataListeners = new Set();
723
+ constructor(router, route, opts) {
724
+ Object.assign(this, {
725
+ route,
726
+ router,
727
+ matchId: opts.matchId,
728
+ pathname: opts.pathname,
729
+ params: opts.params,
730
+ store: createStore({
731
+ routeSearch: {},
732
+ search: {},
733
+ status: 'idle',
734
+ routeLoaderData: {},
735
+ loaderData: {},
736
+ isFetching: false,
737
+ invalid: false,
738
+ invalidAt: Infinity
739
+ })
1821
740
  });
741
+ if (!this.__hasLoaders()) {
742
+ this.store.setState(s => s.status = 'success');
743
+ }
1822
744
  }
1823
- function updateLoaderData() {
1824
- setStore(s => {
1825
- var _store$parentMatch;
1826
- s.loaderData = sharedClone(s.loaderData, {
1827
- ...((_store$parentMatch = store.parentMatch) == null ? void 0 : _store$parentMatch.store.loaderData),
1828
- ...s.routeLoaderData
745
+ #setLoaderData = loaderData => {
746
+ batch(() => {
747
+ this.store.setState(s => {
748
+ s.routeLoaderData = loaderData;
1829
749
  });
750
+ this.#updateLoaderData();
1830
751
  });
1831
- }
1832
- const [store, setStore] = createStore({
1833
- routeSearch: {},
1834
- search: {},
1835
- status: 'idle',
1836
- routeLoaderData: {},
1837
- loaderData: {},
1838
- isFetching: false,
1839
- invalid: false,
1840
- invalidAt: Infinity,
1841
- get isInvalid() {
1842
- const now = Date.now();
1843
- return this.invalid || this.invalidAt < now;
1844
- }
1845
- });
1846
- const routeMatch = {
1847
- ...route,
1848
- ...opts,
1849
- store,
1850
- // setStore,
1851
- router,
1852
- childMatches: [],
1853
- __: {
1854
- setParentMatch: parentMatch => {
1855
- batch(() => {
1856
- setStore(s => {
1857
- s.parentMatch = parentMatch;
1858
- });
1859
- updateLoaderData();
1860
- });
1861
- },
1862
- abortController: new AbortController(),
1863
- validate: () => {
1864
- var _store$parentMatch2;
1865
- // Validate the search params and stabilize them
1866
- const parentSearch = ((_store$parentMatch2 = store.parentMatch) == null ? void 0 : _store$parentMatch2.store.search) ?? router.store.currentLocation.search;
1867
- try {
1868
- const prevSearch = store.routeSearch;
1869
- const validator = typeof routeMatch.options.validateSearch === 'object' ? routeMatch.options.validateSearch.parse : routeMatch.options.validateSearch;
1870
- let nextSearch = sharedClone(prevSearch, (validator == null ? void 0 : validator(parentSearch)) ?? {});
1871
- batch(() => {
1872
- // Invalidate route matches when search param stability changes
1873
- if (prevSearch !== nextSearch) {
1874
- setStore(s => s.invalid = true);
1875
- }
1876
-
1877
- // TODO: Alright, do we need batch() here?
1878
- setStore(s => {
1879
- s.routeSearch = nextSearch;
1880
- s.search = sharedClone(parentSearch, {
1881
- ...parentSearch,
1882
- ...nextSearch
1883
- });
1884
- });
1885
- });
1886
- componentTypes.map(async type => {
1887
- const component = routeMatch.options[type];
1888
- if (typeof routeMatch.__[type] !== 'function') {
1889
- routeMatch.__[type] = component;
1890
- }
1891
- });
1892
- } catch (err) {
1893
- console.error(err);
1894
- const error = new Error('Invalid search params found', {
1895
- cause: err
1896
- });
1897
- error.code = 'INVALID_SEARCH_PARAMS';
1898
- setStore(s => {
1899
- s.status = 'error';
1900
- s.error = error;
1901
- });
1902
-
1903
- // Do not proceed with loading the route
1904
- return;
1905
- }
752
+ };
753
+ cancel = () => {
754
+ this.abortController?.abort();
755
+ };
756
+ load = async loaderOpts => {
757
+ const now = Date.now();
758
+ const minMaxAge = loaderOpts?.preload ? Math.max(loaderOpts?.maxAge, loaderOpts?.gcMaxAge) : 0;
759
+
760
+ // If this is a preload, add it to the preload cache
761
+ if (loaderOpts?.preload && minMaxAge > 0) {
762
+ // If the match is currently active, don't preload it
763
+ if (this.router.store.state.currentMatches.find(d => d.id === this.id)) {
764
+ return;
1906
765
  }
1907
- },
1908
- cancel: () => {
1909
- var _routeMatch$__$abortC;
1910
- (_routeMatch$__$abortC = routeMatch.__.abortController) == null ? void 0 : _routeMatch$__$abortC.abort();
1911
- },
1912
- invalidate: () => {
1913
- setStore(s => s.invalid = true);
1914
- },
1915
- hasLoaders: () => {
1916
- return !!(route.options.loader || componentTypes.some(d => {
1917
- var _route$options$d;
1918
- return (_route$options$d = route.options[d]) == null ? void 0 : _route$options$d.preload;
1919
- }));
1920
- },
1921
- load: async loaderOpts => {
1922
- const now = Date.now();
1923
- const minMaxAge = loaderOpts != null && loaderOpts.preload ? Math.max(loaderOpts == null ? void 0 : loaderOpts.maxAge, loaderOpts == null ? void 0 : loaderOpts.gcMaxAge) : 0;
1924
-
1925
- // If this is a preload, add it to the preload cache
1926
- if (loaderOpts != null && loaderOpts.preload && minMaxAge > 0) {
1927
- // If the match is currently active, don't preload it
1928
- if (router.store.currentMatches.find(d => d.matchId === routeMatch.matchId)) {
1929
- return;
1930
- }
1931
- router.store.matchCache[routeMatch.matchId] = {
766
+ this.router.store.setState(s => {
767
+ s.matchCache[this.id] = {
1932
768
  gc: now + loaderOpts.gcMaxAge,
1933
- match: routeMatch
1934
- };
1935
- }
1936
-
1937
- // If the match is invalid, errored or idle, trigger it to load
1938
- if (store.status === 'success' && store.isInvalid || store.status === 'error' || store.status === 'idle') {
1939
- const maxAge = loaderOpts != null && loaderOpts.preload ? loaderOpts == null ? void 0 : loaderOpts.maxAge : undefined;
1940
- await routeMatch.fetch({
1941
- maxAge
1942
- });
1943
- }
1944
- },
1945
- fetch: async opts => {
1946
- const loadId = '' + Date.now() + Math.random();
1947
- latestId = loadId;
1948
- const checkLatest = async () => {
1949
- if (loadId !== latestId) {
1950
- // warning(true, 'Data loader is out of date!')
1951
- return new Promise(() => {});
1952
- }
1953
- };
1954
- batch(() => {
1955
- // If the match was in an error state, set it
1956
- // to a loading state again. Otherwise, keep it
1957
- // as loading or resolved
1958
- if (store.status === 'idle') {
1959
- setStore(s => s.status = 'loading');
1960
- }
1961
-
1962
- // We started loading the route, so it's no longer invalid
1963
- setStore(s => s.invalid = false);
1964
- });
1965
- routeMatch.__.loadPromise = new Promise(async r => {
1966
- // We are now fetching, even if it's in the background of a
1967
- // resolved state
1968
- setStore(s => s.isFetching = true);
1969
- resolve = r;
1970
- componentsPromise = (async () => {
1971
- // then run all component and data loaders in parallel
1972
- // For each component type, potentially load it asynchronously
1973
-
1974
- await Promise.all(componentTypes.map(async type => {
1975
- var _routeMatch$__$type;
1976
- const component = routeMatch.options[type];
1977
- if ((_routeMatch$__$type = routeMatch.__[type]) != null && _routeMatch$__$type.preload) {
1978
- routeMatch.__[type] = await router.options.loadComponent(component);
1979
- }
1980
- }));
1981
- })();
1982
- dataPromise = Promise.resolve().then(async () => {
1983
- try {
1984
- if (routeMatch.options.loader) {
1985
- const data = await router.loadMatchData(routeMatch);
1986
- await checkLatest();
1987
- setLoaderData(data);
1988
- }
1989
- setStore(s => {
1990
- s.error = undefined;
1991
- s.status = 'success';
1992
- s.updatedAt = Date.now();
1993
- s.invalidAt = s.updatedAt + ((opts == null ? void 0 : opts.maxAge) ?? routeMatch.options.loaderMaxAge ?? router.options.defaultLoaderMaxAge ?? 0);
1994
- });
1995
- return store.routeLoaderData;
1996
- } catch (err) {
1997
- await checkLatest();
1998
- {
1999
- console.error(err);
2000
- }
2001
- setStore(s => {
2002
- s.error = err;
2003
- s.status = 'error';
2004
- s.updatedAt = Date.now();
2005
- });
2006
- throw err;
2007
- }
2008
- });
2009
- const after = async () => {
2010
- await checkLatest();
2011
- setStore(s => s.isFetching = false);
2012
- delete routeMatch.__.loadPromise;
2013
- resolve();
769
+ match: this
2014
770
  };
2015
- try {
2016
- await Promise.all([componentsPromise, dataPromise.catch(() => {})]);
2017
- after();
2018
- } catch {
2019
- after();
2020
- }
2021
- });
2022
- await routeMatch.__.loadPromise;
2023
- await checkLatest();
2024
- }
2025
- };
2026
- if (!routeMatch.hasLoaders()) {
2027
- setStore(s => s.status = 'success');
2028
- }
2029
- return routeMatch;
2030
- }
2031
-
2032
- const defaultParseSearch = parseSearchWith(JSON.parse);
2033
- const defaultStringifySearch = stringifySearchWith(JSON.stringify);
2034
- function parseSearchWith(parser) {
2035
- return searchStr => {
2036
- if (searchStr.substring(0, 1) === '?') {
2037
- searchStr = searchStr.substring(1);
2038
- }
2039
- let query = decode(searchStr);
2040
-
2041
- // Try to parse any query params that might be json
2042
- for (let key in query) {
2043
- const value = query[key];
2044
- if (typeof value === 'string') {
2045
- try {
2046
- query[key] = parser(value);
2047
- } catch (err) {
2048
- //
2049
- }
2050
- }
2051
- }
2052
- return query;
2053
- };
2054
- }
2055
- function stringifySearchWith(stringify) {
2056
- return search => {
2057
- search = {
2058
- ...search
2059
- };
2060
- if (search) {
2061
- Object.keys(search).forEach(key => {
2062
- const val = search[key];
2063
- if (typeof val === 'undefined' || val === undefined) {
2064
- delete search[key];
2065
- } else if (val && typeof val === 'object' && val !== null) {
2066
- try {
2067
- search[key] = stringify(val);
2068
- } catch (err) {
2069
- // silent
2070
- }
2071
- }
2072
- });
2073
- }
2074
- const searchStr = encode(search).toString();
2075
- return searchStr ? `?${searchStr}` : '';
2076
- };
2077
- }
2078
-
2079
- var _window$document;
2080
- // Detect if we're in the DOM
2081
- const isServer = typeof window === 'undefined' || !((_window$document = window.document) != null && _window$document.createElement);
2082
-
2083
- // This is the default history object if none is defined
2084
- const createDefaultHistory = () => isServer ? createMemoryHistory() : createBrowserHistory();
2085
- function getInitialRouterState() {
2086
- return {
2087
- status: 'idle',
2088
- latestLocation: null,
2089
- currentLocation: null,
2090
- currentMatches: [],
2091
- actions: {},
2092
- loaders: {},
2093
- lastUpdated: Date.now(),
2094
- matchCache: {},
2095
- get isFetching() {
2096
- return this.status === 'loading' || this.currentMatches.some(d => d.store.isFetching);
2097
- },
2098
- get isPreloading() {
2099
- return Object.values(this.matchCache).some(d => d.match.store.isFetching && !this.currentMatches.find(dd => dd.matchId === d.match.matchId));
2100
- }
2101
- };
2102
- }
2103
- function createRouter(userOptions) {
2104
- const originalOptions = {
2105
- defaultLoaderGcMaxAge: 5 * 60 * 1000,
2106
- defaultLoaderMaxAge: 0,
2107
- defaultPreloadMaxAge: 2000,
2108
- defaultPreloadDelay: 50,
2109
- context: undefined,
2110
- ...userOptions,
2111
- stringifySearch: (userOptions == null ? void 0 : userOptions.stringifySearch) ?? defaultStringifySearch,
2112
- parseSearch: (userOptions == null ? void 0 : userOptions.parseSearch) ?? defaultParseSearch
2113
- };
2114
- const [store, setStore] = createStore(getInitialRouterState());
2115
- let navigationPromise;
2116
- let startedLoadingAt = Date.now();
2117
- let resolveNavigation = () => {};
2118
- function onFocus() {
2119
- router.load();
2120
- }
2121
- function buildRouteTree(rootRouteConfig) {
2122
- const recurseRoutes = (routeConfigs, parent) => {
2123
- return routeConfigs.map((routeConfig, i) => {
2124
- const routeOptions = routeConfig.options;
2125
- const route = createRoute(routeConfig, routeOptions, i, parent, router);
2126
- const existingRoute = router.routesById[route.routeId];
2127
- if (existingRoute) {
2128
- {
2129
- console.warn(`Duplicate routes found with id: ${String(route.routeId)}`, router.routesById, route);
2130
- }
2131
- throw new Error();
2132
- }
2133
- router.routesById[route.routeId] = route;
2134
- const children = routeConfig.children;
2135
- route.childRoutes = children != null && children.length ? recurseRoutes(children, route) : undefined;
2136
- return route;
2137
- });
2138
- };
2139
- const routes = recurseRoutes([rootRouteConfig]);
2140
- return routes[0];
2141
- }
2142
- function parseLocation(location, previousLocation) {
2143
- const parsedSearch = router.options.parseSearch(location.search);
2144
- return {
2145
- pathname: location.pathname,
2146
- searchStr: location.search,
2147
- search: sharedClone(previousLocation == null ? void 0 : previousLocation.search, parsedSearch),
2148
- hash: location.hash.split('#').reverse()[0] ?? '',
2149
- href: `${location.pathname}${location.search}${location.hash}`,
2150
- state: location.state,
2151
- key: location.key
2152
- };
2153
- }
2154
- function navigate(location) {
2155
- const next = router.buildNext(location);
2156
- return commitLocation(next, location.replace);
2157
- }
2158
- function buildLocation(dest) {
2159
- var _last, _dest$__preSearchFilt, _dest$__preSearchFilt2, _dest$__postSearchFil;
2160
- if (dest === void 0) {
2161
- dest = {};
2162
- }
2163
- const fromPathname = dest.fromCurrent ? store.latestLocation.pathname : dest.from ?? store.latestLocation.pathname;
2164
- let pathname = resolvePath(router.basepath ?? '/', fromPathname, `${dest.to ?? '.'}`);
2165
- const fromMatches = router.matchRoutes(store.latestLocation.pathname, {
2166
- strictParseParams: true
2167
- });
2168
- const toMatches = router.matchRoutes(pathname);
2169
- const prevParams = {
2170
- ...((_last = last(fromMatches)) == null ? void 0 : _last.params)
2171
- };
2172
- let nextParams = (dest.params ?? true) === true ? prevParams : functionalUpdate(dest.params, prevParams);
2173
- if (nextParams) {
2174
- toMatches.map(d => d.options.stringifyParams).filter(Boolean).forEach(fn => {
2175
- Object.assign({}, nextParams, fn(nextParams));
2176
771
  });
2177
772
  }
2178
- pathname = interpolatePath(pathname, nextParams ?? {});
2179
-
2180
- // Pre filters first
2181
- const preFilteredSearch = (_dest$__preSearchFilt = dest.__preSearchFilters) != null && _dest$__preSearchFilt.length ? dest.__preSearchFilters.reduce((prev, next) => next(prev), store.latestLocation.search) : store.latestLocation.search;
2182
-
2183
- // Then the link/navigate function
2184
- const destSearch = dest.search === true ? preFilteredSearch // Preserve resolvedFrom true
2185
- : dest.search ? functionalUpdate(dest.search, preFilteredSearch) ?? {} // Updater
2186
- : (_dest$__preSearchFilt2 = dest.__preSearchFilters) != null && _dest$__preSearchFilt2.length ? preFilteredSearch // Preserve resolvedFrom filters
2187
- : {};
2188
-
2189
- // Then post filters
2190
- const postFilteredSearch = (_dest$__postSearchFil = dest.__postSearchFilters) != null && _dest$__postSearchFil.length ? dest.__postSearchFilters.reduce((prev, next) => next(prev), destSearch) : destSearch;
2191
- const search = sharedClone(store.latestLocation.search, postFilteredSearch);
2192
- const searchStr = router.options.stringifySearch(search);
2193
- let hash = dest.hash === true ? store.latestLocation.hash : functionalUpdate(dest.hash, store.latestLocation.hash);
2194
- hash = hash ? `#${hash}` : '';
2195
- return {
2196
- pathname,
2197
- search,
2198
- searchStr,
2199
- state: store.latestLocation.state,
2200
- hash,
2201
- href: `${pathname}${searchStr}${hash}`,
2202
- key: dest.key
2203
- };
2204
- }
2205
- function commitLocation(next, replace) {
2206
- const id = '' + Date.now() + Math.random();
2207
- let nextAction = 'replace';
2208
- if (!replace) {
2209
- nextAction = 'push';
2210
- }
2211
- const isSameUrl = parseLocation(router.history.location).href === next.href;
2212
- if (isSameUrl && !next.key) {
2213
- nextAction = 'replace';
2214
- }
2215
- router.history[nextAction]({
2216
- pathname: next.pathname,
2217
- hash: next.hash,
2218
- search: next.searchStr
2219
- }, {
2220
- id,
2221
- ...next.state
2222
- });
2223
- return navigationPromise = new Promise(resolve => {
2224
- const previousNavigationResolve = resolveNavigation;
2225
- resolveNavigation = () => {
2226
- previousNavigationResolve();
2227
- resolve();
2228
- };
2229
- });
2230
- }
2231
- const router = {
2232
- types: undefined,
2233
- // public api
2234
- history: (userOptions == null ? void 0 : userOptions.history) || createDefaultHistory(),
2235
- store,
2236
- setStore,
2237
- options: originalOptions,
2238
- basepath: '',
2239
- routeTree: undefined,
2240
- routesById: {},
2241
- reset: () => {
2242
- setStore(s => Object.assign(s, getInitialRouterState()));
2243
- },
2244
- getRoute: id => {
2245
- return router.routesById[id];
2246
- },
2247
- dehydrate: () => {
2248
- return {
2249
- store: {
2250
- ...pick(store, ['latestLocation', 'currentLocation', 'status', 'lastUpdated']),
2251
- currentMatches: store.currentMatches.map(match => ({
2252
- matchId: match.matchId,
2253
- store: pick(match.store, ['status', 'routeLoaderData', 'isInvalid', 'invalidAt'])
2254
- }))
2255
- },
2256
- context: router.options.context
2257
- };
2258
- },
2259
- hydrate: dehydratedRouter => {
2260
- setStore(s => {
2261
- // Update the context TODO: make this part of state?
2262
- router.options.context = dehydratedRouter.context;
2263
-
2264
- // Match the routes
2265
- const currentMatches = router.matchRoutes(dehydratedRouter.store.latestLocation.pathname, {
2266
- strictParseParams: true
2267
- });
2268
- currentMatches.forEach((match, index) => {
2269
- const dehydratedMatch = dehydratedRouter.store.currentMatches[index];
2270
- invariant(dehydratedMatch && dehydratedMatch.matchId === match.matchId, 'Oh no! There was a hydration mismatch when attempting to restore the state of the router! 😬');
2271
- Object.assign(match, dehydratedMatch);
2272
- });
2273
- currentMatches.forEach(match => match.__.validate());
2274
- Object.assign(s, {
2275
- ...dehydratedRouter.store,
2276
- currentMatches
2277
- });
2278
- });
2279
- },
2280
- mount: () => {
2281
- // Mount only does anything on the client
2282
- if (!isServer) {
2283
- // If the router matches are empty, load the matches
2284
- if (!store.currentMatches.length) {
2285
- router.load();
2286
- }
2287
- const unsub = router.history.listen(event => {
2288
- router.load(parseLocation(event.location, store.latestLocation));
2289
- });
2290
-
2291
- // addEventListener does not exist in React Native, but window does
2292
- // In the future, we might need to invert control here for more adapters
2293
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
2294
- if (window.addEventListener) {
2295
- // Listen to visibilitychange and focus
2296
- window.addEventListener('visibilitychange', onFocus, false);
2297
- window.addEventListener('focus', onFocus, false);
2298
- }
2299
- return () => {
2300
- unsub();
2301
- if (window.removeEventListener) {
2302
- // Be sure to unsubscribe if a new handler is set
2303
- window.removeEventListener('visibilitychange', onFocus);
2304
- window.removeEventListener('focus', onFocus);
2305
- }
2306
- };
2307
- }
2308
- return () => {};
2309
- },
2310
- update: opts => {
2311
- const newHistory = (opts == null ? void 0 : opts.history) !== router.history;
2312
- if (!store.latestLocation || newHistory) {
2313
- if (opts != null && opts.history) {
2314
- router.history = opts.history;
2315
- }
2316
- setStore(s => {
2317
- s.latestLocation = parseLocation(router.history.location);
2318
- s.currentLocation = s.latestLocation;
2319
- });
2320
- }
2321
- Object.assign(router.options, opts);
2322
- const {
2323
- basepath,
2324
- routeConfig
2325
- } = router.options;
2326
- router.basepath = `/${trimPath(basepath ?? '') ?? ''}`;
2327
- if (routeConfig) {
2328
- router.routesById = {};
2329
- router.routeTree = buildRouteTree(routeConfig);
2330
- }
2331
- return router;
2332
- },
2333
- cancelMatches: () => {
2334
- [...store.currentMatches, ...(store.pendingMatches || [])].forEach(match => {
2335
- match.cancel();
773
+
774
+ // If the match is invalid, errored or idle, trigger it to load
775
+ if (this.store.state.status === 'success' && this.getIsInvalid() || this.store.state.status === 'error' || this.store.state.status === 'idle') {
776
+ const maxAge = loaderOpts?.preload ? loaderOpts?.maxAge : undefined;
777
+ await this.fetch({
778
+ maxAge
2336
779
  });
2337
- },
2338
- load: async next => {
2339
- let now = Date.now();
2340
- const startedAt = now;
2341
- startedLoadingAt = startedAt;
2342
-
2343
- // Cancel any pending matches
2344
- router.cancelMatches();
2345
- let matches;
780
+ }
781
+ };
782
+ fetch = async opts => {
783
+ this.__loadPromise = new Promise(async resolve => {
784
+ const loadId = '' + Date.now() + Math.random();
785
+ this.#latestId = loadId;
786
+ const checkLatest = () => loadId !== this.#latestId ? this.__loadPromise?.then(() => resolve()) : undefined;
787
+ let latestPromise;
2346
788
  batch(() => {
2347
- if (next) {
2348
- // Ingest the new location
2349
- setStore(s => {
2350
- s.latestLocation = next;
2351
- });
789
+ // If the match was in an error state, set it
790
+ // to a loading state again. Otherwise, keep it
791
+ // as loading or resolved
792
+ if (this.store.state.status === 'idle') {
793
+ this.store.setState(s => s.status = 'loading');
2352
794
  }
2353
795
 
2354
- // Match the routes
2355
- matches = router.matchRoutes(store.latestLocation.pathname, {
2356
- strictParseParams: true
2357
- });
2358
- console.log('set loading', matches);
2359
- setStore(s => {
2360
- s.status = 'loading';
2361
- s.pendingMatches = matches;
2362
- s.pendingLocation = store.latestLocation;
2363
- });
796
+ // We started loading the route, so it's no longer invalid
797
+ this.store.setState(s => s.invalid = false);
2364
798
  });
2365
799
 
2366
- // Load the matches
800
+ // We are now fetching, even if it's in the background of a
801
+ // resolved state
802
+ this.store.setState(s => s.isFetching = true);
803
+ this.#resolve = resolve;
804
+ const componentsPromise = (async () => {
805
+ // then run all component and data loaders in parallel
806
+ // For each component type, potentially load it asynchronously
807
+
808
+ await Promise.all(componentTypes.map(async type => {
809
+ const component = this.route.options[type];
810
+ if (this[type]?.preload) {
811
+ this[type] = await this.router.options.loadComponent(component);
812
+ }
813
+ }));
814
+ })();
815
+ const dataPromise = Promise.resolve().then(async () => {
816
+ try {
817
+ if (this.route.options.loader) {
818
+ const data = await this.router.loadMatchData(this);
819
+ if (latestPromise = checkLatest()) return latestPromise;
820
+ this.#setLoaderData(data);
821
+ }
822
+ this.store.setState(s => {
823
+ s.error = undefined;
824
+ s.status = 'success';
825
+ s.updatedAt = Date.now();
826
+ s.invalidAt = s.updatedAt + (opts?.maxAge ?? this.route.options.loaderMaxAge ?? this.router.options.defaultLoaderMaxAge ?? 0);
827
+ });
828
+ return this.store.state.routeLoaderData;
829
+ } catch (err) {
830
+ if (latestPromise = checkLatest()) return latestPromise;
831
+ {
832
+ console.error(err);
833
+ }
834
+ this.store.setState(s => {
835
+ s.error = err;
836
+ s.status = 'error';
837
+ s.updatedAt = Date.now();
838
+ });
839
+ throw err;
840
+ }
841
+ });
842
+ const after = async () => {
843
+ if (latestPromise = checkLatest()) return latestPromise;
844
+ this.store.setState(s => s.isFetching = false);
845
+ this.#resolve();
846
+ delete this.__loadPromise;
847
+ };
2367
848
  try {
2368
- await router.loadMatches(matches);
2369
- } catch (err) {
2370
- console.log(err);
2371
- invariant(false, 'Matches failed to load due to error above ☝️. Navigation cancelled!');
2372
- }
2373
- if (startedLoadingAt !== startedAt) {
2374
- // Ignore side-effects of outdated side-effects
2375
- return navigationPromise;
849
+ await Promise.all([componentsPromise, dataPromise.catch(() => {})]);
850
+ after();
851
+ } catch {
852
+ after();
2376
853
  }
2377
- const previousMatches = store.currentMatches;
2378
- const exiting = [],
2379
- staying = [];
2380
- previousMatches.forEach(d => {
2381
- if (matches.find(dd => dd.matchId === d.matchId)) {
2382
- staying.push(d);
2383
- } else {
2384
- exiting.push(d);
2385
- }
854
+ });
855
+ return this.__loadPromise;
856
+ };
857
+ invalidate = async () => {
858
+ this.store.setState(s => s.invalid = true);
859
+ if (this.router.store.state.currentMatches.find(d => d.id === this.id)) {
860
+ await this.load();
861
+ }
862
+ };
863
+ __hasLoaders = () => {
864
+ return !!(this.route.options.loader || componentTypes.some(d => this.route.options[d]?.preload));
865
+ };
866
+ getIsInvalid = () => {
867
+ const now = Date.now();
868
+ return this.store.state.invalid || this.store.state.invalidAt < now;
869
+ };
870
+ #updateLoaderData = () => {
871
+ this.store.setState(s => {
872
+ s.loaderData = replaceEqualDeep(s.loaderData, {
873
+ ...this.parentMatch?.store.state.loaderData,
874
+ ...s.routeLoaderData
2386
875
  });
2387
- const entering = matches.filter(d => {
2388
- return !previousMatches.find(dd => dd.matchId === d.matchId);
876
+ });
877
+ this.onLoaderDataListeners.forEach(listener => listener());
878
+ };
879
+ __setParentMatch = parentMatch => {
880
+ if (!this.parentMatch && parentMatch) {
881
+ this.parentMatch = parentMatch;
882
+ this.parentMatch.__onLoaderData(() => {
883
+ this.#updateLoaderData();
2389
884
  });
2390
- now = Date.now();
2391
- exiting.forEach(d => {
2392
- d.__.onExit == null ? void 0 : d.__.onExit({
2393
- params: d.params,
2394
- search: d.store.routeSearch
2395
- });
885
+ }
886
+ };
887
+ __onLoaderData = listener => {
888
+ this.onLoaderDataListeners.add(listener);
889
+ // return () => this.onLoaderDataListeners.delete(listener)
890
+ };
2396
891
 
2397
- // Clear idle error states when match leaves
2398
- if (d.store.status === 'error' && !d.store.isFetching) {
2399
- d.store.status = 'idle';
2400
- d.store.error = undefined;
892
+ __validate = () => {
893
+ // Validate the search params and stabilize them
894
+ const parentSearch = this.parentMatch?.store.state.search ?? this.router.store.state.latestLocation.search;
895
+ try {
896
+ const prevSearch = this.store.state.routeSearch;
897
+ const validator = typeof this.route.options.validateSearch === 'object' ? this.route.options.validateSearch.parse : this.route.options.validateSearch;
898
+ let nextSearch = validator?.(parentSearch) ?? {};
899
+ batch(() => {
900
+ // Invalidate route matches when search param stability changes
901
+ if (prevSearch !== nextSearch) {
902
+ this.store.setState(s => s.invalid = true);
2401
903
  }
2402
- const gc = Math.max(d.options.loaderGcMaxAge ?? router.options.defaultLoaderGcMaxAge ?? 0, d.options.loaderMaxAge ?? router.options.defaultLoaderMaxAge ?? 0);
2403
- if (gc > 0) {
2404
- store.matchCache[d.matchId] = {
2405
- gc: gc == Infinity ? Number.MAX_SAFE_INTEGER : now + gc,
2406
- match: d
904
+ this.store.setState(s => {
905
+ s.routeSearch = nextSearch;
906
+ s.search = {
907
+ ...parentSearch,
908
+ ...nextSearch
2407
909
  };
910
+ });
911
+ });
912
+ componentTypes.map(async type => {
913
+ const component = this.route.options[type];
914
+ if (typeof this[type] !== 'function') {
915
+ this[type] = component;
2408
916
  }
2409
917
  });
2410
- staying.forEach(d => {
2411
- d.options.onTransition == null ? void 0 : d.options.onTransition({
2412
- params: d.params,
2413
- search: d.store.routeSearch
2414
- });
918
+ } catch (err) {
919
+ console.error(err);
920
+ const error = new Error('Invalid search params found', {
921
+ cause: err
2415
922
  });
2416
- entering.forEach(d => {
2417
- d.__.onExit = d.options.onLoaded == null ? void 0 : d.options.onLoaded({
2418
- params: d.params,
2419
- search: d.store.search
2420
- });
2421
- delete store.matchCache[d.matchId];
923
+ error.code = 'INVALID_SEARCH_PARAMS';
924
+ this.store.setState(s => {
925
+ s.status = 'error';
926
+ s.error = error;
2422
927
  });
2423
- if (startedLoadingAt !== startedAt) {
2424
- // Ignore side-effects of match loading
2425
- return;
928
+
929
+ // Do not proceed with loading the route
930
+ return;
931
+ }
932
+ };
933
+ }
934
+
935
+ const defaultParseSearch = parseSearchWith(JSON.parse);
936
+ const defaultStringifySearch = stringifySearchWith(JSON.stringify);
937
+ function parseSearchWith(parser) {
938
+ return searchStr => {
939
+ if (searchStr.substring(0, 1) === '?') {
940
+ searchStr = searchStr.substring(1);
941
+ }
942
+ let query = decode(searchStr);
943
+
944
+ // Try to parse any query params that might be json
945
+ for (let key in query) {
946
+ const value = query[key];
947
+ if (typeof value === 'string') {
948
+ try {
949
+ query[key] = parser(value);
950
+ } catch (err) {
951
+ //
952
+ }
2426
953
  }
2427
- matches.forEach(match => {
2428
- // Clear actions
2429
- if (match.action) {
2430
- // TODO: Check reactivity here
2431
- match.action.current = undefined;
2432
- match.action.submissions = [];
954
+ }
955
+ return query;
956
+ };
957
+ }
958
+ function stringifySearchWith(stringify) {
959
+ return search => {
960
+ search = {
961
+ ...search
962
+ };
963
+ if (search) {
964
+ Object.keys(search).forEach(key => {
965
+ const val = search[key];
966
+ if (typeof val === 'undefined' || val === undefined) {
967
+ delete search[key];
968
+ } else if (val && typeof val === 'object' && val !== null) {
969
+ try {
970
+ search[key] = stringify(val);
971
+ } catch (err) {
972
+ // silent
973
+ }
2433
974
  }
2434
975
  });
2435
- setStore(s => {
2436
- console.log('set', matches);
2437
- Object.assign(s, {
2438
- status: 'idle',
2439
- currentLocation: store.latestLocation,
2440
- currentMatches: matches,
2441
- pendingLocation: undefined,
2442
- pendingMatches: undefined
2443
- });
2444
- });
2445
- resolveNavigation();
2446
- },
2447
- cleanMatchCache: () => {
2448
- const now = Date.now();
2449
- setStore(s => {
2450
- Object.keys(s.matchCache).forEach(matchId => {
2451
- const entry = s.matchCache[matchId];
2452
-
2453
- // Don't remove loading matches
2454
- if (entry.match.store.status === 'loading') {
2455
- return;
2456
- }
976
+ }
977
+ const searchStr = encode(search).toString();
978
+ return searchStr ? `?${searchStr}` : '';
979
+ };
980
+ }
2457
981
 
2458
- // Do not remove successful matches that are still valid
2459
- if (entry.gc > 0 && entry.gc > now) {
2460
- return;
2461
- }
982
+ const defaultFetchServerDataFn = async ({
983
+ router,
984
+ routeMatch
985
+ }) => {
986
+ const next = router.buildNext({
987
+ to: '.',
988
+ search: d => ({
989
+ ...(d ?? {}),
990
+ __data: {
991
+ matchId: routeMatch.id
992
+ }
993
+ })
994
+ });
995
+ const res = await fetch(next.href, {
996
+ method: 'GET',
997
+ signal: routeMatch.abortController.signal
998
+ });
999
+ if (res.ok) {
1000
+ return res.json();
1001
+ }
1002
+ throw new Error('Failed to fetch match data');
1003
+ };
1004
+ class Router {
1005
+ // __location: Location<TAllRouteInfo['fullSearchSchema']>
1006
+
1007
+ startedLoadingAt = Date.now();
1008
+ resolveNavigation = () => {};
1009
+ constructor(options) {
1010
+ this.options = {
1011
+ defaultLoaderGcMaxAge: 5 * 60 * 1000,
1012
+ defaultLoaderMaxAge: 0,
1013
+ defaultPreloadMaxAge: 2000,
1014
+ defaultPreloadDelay: 50,
1015
+ context: undefined,
1016
+ ...options,
1017
+ stringifySearch: options?.stringifySearch ?? defaultStringifySearch,
1018
+ parseSearch: options?.parseSearch ?? defaultParseSearch,
1019
+ fetchServerDataFn: options?.fetchServerDataFn ?? defaultFetchServerDataFn
1020
+ };
1021
+ this.history = this.options?.history ?? isServer ? createMemoryHistory() : createBrowserHistory();
1022
+ this.store = createStore(getInitialRouterState());
1023
+ this.basepath = '';
1024
+ this.update(options);
2462
1025
 
2463
- // Everything else gets removed
2464
- delete s.matchCache[matchId];
2465
- });
1026
+ // Allow frameworks to hook into the router creation
1027
+ this.options.createRouter?.(this);
1028
+ }
1029
+ reset = () => {
1030
+ this.store.setState(s => Object.assign(s, getInitialRouterState()));
1031
+ };
1032
+ mount = () => {
1033
+ // Mount only does anything on the client
1034
+ if (!isServer) {
1035
+ // If the router matches are empty, load the matches
1036
+ if (!this.store.state.currentMatches.length) {
1037
+ this.load();
1038
+ }
1039
+ const unsubHistory = this.history.listen(() => {
1040
+ this.load(this.#parseLocation(this.store.state.latestLocation));
2466
1041
  });
2467
- },
2468
- loadRoute: async function (navigateOpts) {
2469
- if (navigateOpts === void 0) {
2470
- navigateOpts = store.latestLocation;
1042
+ const visibilityChangeEvent = 'visibilitychange';
1043
+ const focusEvent = 'focus';
1044
+
1045
+ // addEventListener does not exist in React Native, but window does
1046
+ // In the future, we might need to invert control here for more adapters
1047
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
1048
+ if (window.addEventListener) {
1049
+ // Listen to visibilitychange and focus
1050
+ window.addEventListener(visibilityChangeEvent, this.#onFocus, false);
1051
+ window.addEventListener(focusEvent, this.#onFocus, false);
2471
1052
  }
2472
- const next = router.buildNext(navigateOpts);
2473
- const matches = router.matchRoutes(next.pathname, {
2474
- strictParseParams: true
1053
+ return () => {
1054
+ unsubHistory();
1055
+ if (window.removeEventListener) {
1056
+ // Be sure to unsubscribe if a new handler is set
1057
+
1058
+ window.removeEventListener(visibilityChangeEvent, this.#onFocus);
1059
+ window.removeEventListener(focusEvent, this.#onFocus);
1060
+ }
1061
+ };
1062
+ }
1063
+ return () => {};
1064
+ };
1065
+ update = opts => {
1066
+ if (!this.store.state.latestLocation) {
1067
+ this.store.setState(s => {
1068
+ s.latestLocation = this.#parseLocation();
1069
+ s.currentLocation = s.latestLocation;
2475
1070
  });
2476
- await router.loadMatches(matches);
2477
- return matches;
2478
- },
2479
- preloadRoute: async function (navigateOpts, loaderOpts) {
2480
- if (navigateOpts === void 0) {
2481
- navigateOpts = store.latestLocation;
1071
+ }
1072
+ Object.assign(this.options, opts);
1073
+ const {
1074
+ basepath,
1075
+ routeConfig
1076
+ } = this.options;
1077
+ this.basepath = `/${trimPath(basepath ?? '') ?? ''}`;
1078
+ if (routeConfig) {
1079
+ this.routesById = {};
1080
+ this.routeTree = this.#buildRouteTree(routeConfig);
1081
+ }
1082
+ return this;
1083
+ };
1084
+ buildNext = opts => {
1085
+ const next = this.#buildLocation(opts);
1086
+ const matches = this.matchRoutes(next.pathname);
1087
+ const __preSearchFilters = matches.map(match => match.route.options.preSearchFilters ?? []).flat().filter(Boolean);
1088
+ const __postSearchFilters = matches.map(match => match.route.options.postSearchFilters ?? []).flat().filter(Boolean);
1089
+ return this.#buildLocation({
1090
+ ...opts,
1091
+ __preSearchFilters,
1092
+ __postSearchFilters
1093
+ });
1094
+ };
1095
+ cancelMatches = () => {
1096
+ [...this.store.state.currentMatches, ...(this.store.state.pendingMatches || [])].forEach(match => {
1097
+ match.cancel();
1098
+ });
1099
+ };
1100
+ load = async next => {
1101
+ let now = Date.now();
1102
+ const startedAt = now;
1103
+ this.startedLoadingAt = startedAt;
1104
+
1105
+ // Cancel any pending matches
1106
+ this.cancelMatches();
1107
+ let matches;
1108
+ batch(() => {
1109
+ if (next) {
1110
+ // Ingest the new location
1111
+ this.store.setState(s => {
1112
+ s.latestLocation = next;
1113
+ });
2482
1114
  }
2483
- const next = router.buildNext(navigateOpts);
2484
- const matches = router.matchRoutes(next.pathname, {
1115
+
1116
+ // Match the routes
1117
+ matches = this.matchRoutes(this.store.state.latestLocation.pathname, {
2485
1118
  strictParseParams: true
2486
1119
  });
2487
- await router.loadMatches(matches, {
2488
- preload: true,
2489
- maxAge: loaderOpts.maxAge ?? router.options.defaultPreloadMaxAge ?? router.options.defaultLoaderMaxAge ?? 0,
2490
- gcMaxAge: loaderOpts.gcMaxAge ?? router.options.defaultPreloadGcMaxAge ?? router.options.defaultLoaderGcMaxAge ?? 0
1120
+ this.store.setState(s => {
1121
+ s.status = 'loading';
1122
+ s.pendingMatches = matches;
1123
+ s.pendingLocation = this.store.state.latestLocation;
2491
1124
  });
2492
- return matches;
2493
- },
2494
- matchRoutes: (pathname, opts) => {
2495
- router.cleanMatchCache();
2496
- const matches = [];
2497
- if (!router.routeTree) {
2498
- return matches;
1125
+ });
1126
+
1127
+ // Load the matches
1128
+ try {
1129
+ await this.loadMatches(matches);
1130
+ } catch (err) {
1131
+ console.warn(err);
1132
+ invariant(false, 'Matches failed to load due to error above ☝️. Navigation cancelled!');
1133
+ }
1134
+ if (this.startedLoadingAt !== startedAt) {
1135
+ // Ignore side-effects of outdated side-effects
1136
+ return this.navigationPromise;
1137
+ }
1138
+ const previousMatches = this.store.state.currentMatches;
1139
+ const exiting = [],
1140
+ staying = [];
1141
+ previousMatches.forEach(d => {
1142
+ if (matches.find(dd => dd.id === d.id)) {
1143
+ staying.push(d);
1144
+ } else {
1145
+ exiting.push(d);
2499
1146
  }
2500
- const existingMatches = [...store.currentMatches, ...(store.pendingMatches ?? [])];
2501
- const recurse = async routes => {
2502
- var _foundRoute$childRout;
2503
- const parentMatch = last(matches);
2504
- let params = (parentMatch == null ? void 0 : parentMatch.params) ?? {};
2505
- const filteredRoutes = (router.options.filterRoutes == null ? void 0 : router.options.filterRoutes(routes)) ?? routes;
2506
- let foundRoutes = [];
2507
- const findMatchInRoutes = (parentRoutes, routes) => {
2508
- routes.some(route => {
2509
- var _route$childRoutes, _route$childRoutes2;
2510
- if (!route.routePath && (_route$childRoutes = route.childRoutes) != null && _route$childRoutes.length) {
2511
- return findMatchInRoutes([...foundRoutes, route], route.childRoutes);
2512
- }
2513
- const fuzzy = !!(route.routePath !== '/' || (_route$childRoutes2 = route.childRoutes) != null && _route$childRoutes2.length);
2514
- const matchParams = matchPathname(router.basepath, pathname, {
2515
- to: route.fullPath,
2516
- fuzzy,
2517
- caseSensitive: route.options.caseSensitive ?? router.options.caseSensitive
2518
- });
2519
- if (matchParams) {
2520
- let parsedParams;
2521
- try {
2522
- parsedParams = (route.options.parseParams == null ? void 0 : route.options.parseParams(matchParams)) ?? matchParams;
2523
- } catch (err) {
2524
- if (opts != null && opts.strictParseParams) {
2525
- throw err;
2526
- }
2527
- }
2528
- params = {
2529
- ...params,
2530
- ...parsedParams
2531
- };
2532
- }
2533
- if (!!matchParams) {
2534
- foundRoutes = [...parentRoutes, route];
2535
- }
2536
- return !!foundRoutes.length;
2537
- });
2538
- return !!foundRoutes.length;
2539
- };
2540
- findMatchInRoutes([], filteredRoutes);
2541
- if (!foundRoutes.length) {
2542
- return;
2543
- }
2544
- foundRoutes.forEach(foundRoute => {
2545
- var _store$matchCache$mat;
2546
- const interpolatedPath = interpolatePath(foundRoute.routePath, params);
2547
- const matchId = interpolatePath(foundRoute.routeId, params, true);
2548
- const match = existingMatches.find(d => d.matchId === matchId) || ((_store$matchCache$mat = store.matchCache[matchId]) == null ? void 0 : _store$matchCache$mat.match) || createRouteMatch(router, foundRoute, {
2549
- parentMatch,
2550
- matchId,
2551
- params,
2552
- pathname: joinPaths([router.basepath, interpolatedPath])
2553
- });
2554
- matches.push(match);
2555
- });
2556
- const foundRoute = last(foundRoutes);
2557
- if ((_foundRoute$childRout = foundRoute.childRoutes) != null && _foundRoute$childRout.length) {
2558
- recurse(foundRoute.childRoutes);
2559
- }
2560
- };
2561
- recurse([router.routeTree]);
2562
- linkMatches(matches);
2563
- return matches;
2564
- },
2565
- loadMatches: async (resolvedMatches, loaderOpts) => {
2566
- resolvedMatches.forEach(async match => {
2567
- // Validate the match (loads search params etc)
2568
- match.__.validate();
1147
+ });
1148
+ const entering = matches.filter(d => {
1149
+ return !previousMatches.find(dd => dd.id === d.id);
1150
+ });
1151
+ now = Date.now();
1152
+ exiting.forEach(d => {
1153
+ d.__onExit?.({
1154
+ params: d.params,
1155
+ search: d.store.state.routeSearch
2569
1156
  });
2570
1157
 
2571
- // Check each match middleware to see if the route can be accessed
2572
- await Promise.all(resolvedMatches.map(async match => {
2573
- try {
2574
- await (match.options.beforeLoad == null ? void 0 : match.options.beforeLoad({
2575
- router: router,
2576
- match
2577
- }));
2578
- } catch (err) {
2579
- if (!(loaderOpts != null && loaderOpts.preload)) {
2580
- match.options.onLoadError == null ? void 0 : match.options.onLoadError(err);
2581
- }
2582
- throw err;
2583
- }
2584
- }));
2585
- const matchPromises = resolvedMatches.map(async match => {
2586
- var _search$__data;
2587
- const search = match.store.search;
2588
- if ((_search$__data = search.__data) != null && _search$__data.matchId && search.__data.matchId !== match.matchId) {
1158
+ // Clear non-loading error states when match leaves
1159
+ if (d.store.state.status === 'error' && !d.store.state.isFetching) {
1160
+ d.store.setState(s => {
1161
+ s.status = 'idle';
1162
+ s.error = undefined;
1163
+ });
1164
+ }
1165
+ const gc = Math.max(d.route.options.loaderGcMaxAge ?? this.options.defaultLoaderGcMaxAge ?? 0, d.route.options.loaderMaxAge ?? this.options.defaultLoaderMaxAge ?? 0);
1166
+ if (gc > 0) {
1167
+ this.store.setState(s => {
1168
+ s.matchCache[d.id] = {
1169
+ gc: gc == Infinity ? Number.MAX_SAFE_INTEGER : now + gc,
1170
+ match: d
1171
+ };
1172
+ });
1173
+ }
1174
+ });
1175
+ staying.forEach(d => {
1176
+ d.route.options.onTransition?.({
1177
+ params: d.params,
1178
+ search: d.store.state.routeSearch
1179
+ });
1180
+ });
1181
+ entering.forEach(d => {
1182
+ d.__onExit = d.route.options.onLoaded?.({
1183
+ params: d.params,
1184
+ search: d.store.state.search
1185
+ });
1186
+ delete this.store.state.matchCache[d.id];
1187
+ });
1188
+ this.store.setState(s => {
1189
+ Object.assign(s, {
1190
+ status: 'idle',
1191
+ currentLocation: this.store.state.latestLocation,
1192
+ currentMatches: matches,
1193
+ pendingLocation: undefined,
1194
+ pendingMatches: undefined
1195
+ });
1196
+ });
1197
+ this.options.onRouteChange?.();
1198
+ this.resolveNavigation();
1199
+ };
1200
+ cleanMatchCache = () => {
1201
+ const now = Date.now();
1202
+ this.store.setState(s => {
1203
+ Object.keys(s.matchCache).forEach(matchId => {
1204
+ const entry = s.matchCache[matchId];
1205
+
1206
+ // Don't remove loading matches
1207
+ if (entry.match.store.state.status === 'loading') {
2589
1208
  return;
2590
1209
  }
2591
- match.load(loaderOpts);
2592
- if (match.store.status !== 'success' && match.__.loadPromise) {
2593
- // Wait for the first sign of activity from the match
2594
- await match.__.loadPromise;
1210
+
1211
+ // Do not remove successful matches that are still valid
1212
+ if (entry.gc > 0 && entry.gc > now) {
1213
+ return;
2595
1214
  }
1215
+
1216
+ // Everything else gets removed
1217
+ delete s.matchCache[matchId];
2596
1218
  });
2597
- await Promise.all(matchPromises);
2598
- },
2599
- loadMatchData: async routeMatch => {
2600
- if (isServer || !router.options.useServerData) {
2601
- return (await (routeMatch.options.loader == null ? void 0 : routeMatch.options.loader({
2602
- // parentLoaderPromise: routeMatch.parentMatch?.__.dataPromise,
2603
- params: routeMatch.params,
2604
- search: routeMatch.store.routeSearch,
2605
- signal: routeMatch.__.abortController.signal
2606
- }))) || {};
2607
- } else {
2608
- const next = router.buildNext({
2609
- to: '.',
2610
- search: d => ({
2611
- ...(d ?? {}),
2612
- __data: {
2613
- matchId: routeMatch.matchId
1219
+ });
1220
+ };
1221
+ getRoute = id => {
1222
+ const route = this.routesById[id];
1223
+ invariant(route, `Route with id "${id}" not found`);
1224
+ return route;
1225
+ };
1226
+ loadRoute = async (navigateOpts = this.store.state.latestLocation) => {
1227
+ const next = this.buildNext(navigateOpts);
1228
+ const matches = this.matchRoutes(next.pathname, {
1229
+ strictParseParams: true
1230
+ });
1231
+ await this.loadMatches(matches);
1232
+ return matches;
1233
+ };
1234
+ preloadRoute = async (navigateOpts = this.store.state.latestLocation, loaderOpts) => {
1235
+ const next = this.buildNext(navigateOpts);
1236
+ const matches = this.matchRoutes(next.pathname, {
1237
+ strictParseParams: true
1238
+ });
1239
+ await this.loadMatches(matches, {
1240
+ preload: true,
1241
+ maxAge: loaderOpts.maxAge ?? this.options.defaultPreloadMaxAge ?? this.options.defaultLoaderMaxAge ?? 0,
1242
+ gcMaxAge: loaderOpts.gcMaxAge ?? this.options.defaultPreloadGcMaxAge ?? this.options.defaultLoaderGcMaxAge ?? 0
1243
+ });
1244
+ return matches;
1245
+ };
1246
+ matchRoutes = (pathname, opts) => {
1247
+ const matches = [];
1248
+ if (!this.routeTree) {
1249
+ return matches;
1250
+ }
1251
+ const existingMatches = [...this.store.state.currentMatches, ...(this.store.state.pendingMatches ?? [])];
1252
+ const recurse = async routes => {
1253
+ const parentMatch = last(matches);
1254
+ let params = parentMatch?.params ?? {};
1255
+ const filteredRoutes = this.options.filterRoutes?.(routes) ?? routes;
1256
+ let foundRoutes = [];
1257
+ const findMatchInRoutes = (parentRoutes, routes) => {
1258
+ routes.some(route => {
1259
+ if (!route.path && route.childRoutes?.length) {
1260
+ return findMatchInRoutes([...foundRoutes, route], route.childRoutes);
1261
+ }
1262
+ const fuzzy = !!(route.path !== '/' || route.childRoutes?.length);
1263
+ const matchParams = matchPathname(this.basepath, pathname, {
1264
+ to: route.fullPath,
1265
+ fuzzy,
1266
+ caseSensitive: route.options.caseSensitive ?? this.options.caseSensitive
1267
+ });
1268
+ if (matchParams) {
1269
+ let parsedParams;
1270
+ try {
1271
+ parsedParams = route.options.parseParams?.(matchParams) ?? matchParams;
1272
+ } catch (err) {
1273
+ if (opts?.strictParseParams) {
1274
+ throw err;
1275
+ }
2614
1276
  }
2615
- })
1277
+ params = {
1278
+ ...params,
1279
+ ...parsedParams
1280
+ };
1281
+ }
1282
+ if (!!matchParams) {
1283
+ foundRoutes = [...parentRoutes, route];
1284
+ }
1285
+ return !!foundRoutes.length;
2616
1286
  });
2617
-
2618
- // Refresh:
2619
- // '/dashboard'
2620
- // '/dashboard/invoices/'
2621
- // '/dashboard/invoices/123'
2622
-
2623
- // New:
2624
- // '/dashboard/invoices/456'
2625
-
2626
- // TODO: batch requests when possible
2627
-
2628
- const res = await fetch(next.href, {
2629
- method: 'GET'
2630
- // signal: routeMatch.__.abortController.signal,
1287
+ return !!foundRoutes.length;
1288
+ };
1289
+ findMatchInRoutes([], filteredRoutes);
1290
+ if (!foundRoutes.length) {
1291
+ return;
1292
+ }
1293
+ foundRoutes.forEach(foundRoute => {
1294
+ const interpolatedPath = interpolatePath(foundRoute.path, params);
1295
+ const matchId = interpolatePath(foundRoute.id, params, true);
1296
+ const match = existingMatches.find(d => d.id === matchId) || this.store.state.matchCache[matchId]?.match || new RouteMatch(this, foundRoute, {
1297
+ matchId,
1298
+ params,
1299
+ pathname: joinPaths([this.basepath, interpolatedPath])
2631
1300
  });
1301
+ matches.push(match);
1302
+ });
1303
+ const foundRoute = last(foundRoutes);
1304
+ if (foundRoute.childRoutes?.length) {
1305
+ recurse(foundRoute.childRoutes);
1306
+ }
1307
+ };
1308
+ recurse([this.routeTree]);
1309
+ linkMatches(matches);
1310
+ return matches;
1311
+ };
1312
+ loadMatches = async (resolvedMatches, loaderOpts) => {
1313
+ this.cleanMatchCache();
1314
+ resolvedMatches.forEach(async match => {
1315
+ // Validate the match (loads search params etc)
1316
+ match.__validate();
1317
+ });
2632
1318
 
2633
- if (res.ok) {
2634
- return res.json();
1319
+ // Check each match middleware to see if the route can be accessed
1320
+ await Promise.all(resolvedMatches.map(async match => {
1321
+ try {
1322
+ await match.route.options.beforeLoad?.({
1323
+ router: this,
1324
+ match
1325
+ });
1326
+ } catch (err) {
1327
+ if (!loaderOpts?.preload) {
1328
+ match.route.options.onLoadError?.(err);
2635
1329
  }
2636
- throw new Error('Failed to fetch match data');
1330
+ throw err;
2637
1331
  }
2638
- },
2639
- invalidateRoute: opts => {
2640
- const next = router.buildNext(opts);
2641
- const unloadedMatchIds = router.matchRoutes(next.pathname).map(d => d.matchId);
2642
- [...store.currentMatches, ...(store.pendingMatches ?? [])].forEach(match => {
2643
- if (unloadedMatchIds.includes(match.matchId)) {
2644
- match.invalidate();
2645
- }
1332
+ }));
1333
+ const matchPromises = resolvedMatches.map(async (match, index) => {
1334
+ const prevMatch = resolvedMatches[1];
1335
+ const search = match.store.state.search;
1336
+ if (search.__data?.matchId && search.__data.matchId !== match.id) {
1337
+ return;
1338
+ }
1339
+ match.load(loaderOpts);
1340
+ if (match.store.state.status !== 'success' && match.__loadPromise) {
1341
+ // Wait for the first sign of activity from the match
1342
+ await match.__loadPromise;
1343
+ }
1344
+ if (prevMatch) {
1345
+ await prevMatch.__loadPromise;
1346
+ }
1347
+ });
1348
+ await Promise.all(matchPromises);
1349
+ };
1350
+ loadMatchData = async routeMatch => {
1351
+ if (isServer || !this.options.useServerData) {
1352
+ return (await routeMatch.route.options.loader?.({
1353
+ // parentLoaderPromise: routeMatch.parentMatch.dataPromise,
1354
+ params: routeMatch.params,
1355
+ search: routeMatch.store.state.routeSearch,
1356
+ signal: routeMatch.abortController.signal
1357
+ })) || {};
1358
+ } else {
1359
+ // Refresh:
1360
+ // '/dashboard'
1361
+ // '/dashboard/invoices/'
1362
+ // '/dashboard/invoices/123'
1363
+
1364
+ // New:
1365
+ // '/dashboard/invoices/456'
1366
+
1367
+ // TODO: batch requests when possible
1368
+
1369
+ return this.options.fetchServerDataFn({
1370
+ router: this,
1371
+ routeMatch
2646
1372
  });
2647
- },
2648
- reload: () => navigate({
1373
+ }
1374
+ };
1375
+ invalidateRoute = async opts => {
1376
+ const next = this.buildNext(opts);
1377
+ const unloadedMatchIds = this.matchRoutes(next.pathname).map(d => d.id);
1378
+ await Promise.allSettled([...this.store.state.currentMatches, ...(this.store.state.pendingMatches ?? [])].map(async match => {
1379
+ if (unloadedMatchIds.includes(match.id)) {
1380
+ return match.invalidate();
1381
+ }
1382
+ }));
1383
+ };
1384
+ reload = () => {
1385
+ this.navigate({
2649
1386
  fromCurrent: true,
2650
1387
  replace: true,
2651
1388
  search: true
2652
- }),
2653
- resolvePath: (from, path) => {
2654
- return resolvePath(router.basepath, from, cleanPath(path));
2655
- },
2656
- matchRoute: (location, opts) => {
2657
- // const location = router.buildNext(opts)
2658
-
2659
- location = {
2660
- ...location,
2661
- to: location.to ? router.resolvePath(location.from ?? '', location.to) : undefined
2662
- };
2663
- const next = router.buildNext(location);
2664
- if (opts != null && opts.pending) {
2665
- if (!store.pendingLocation) {
2666
- return false;
2667
- }
2668
- return !!matchPathname(router.basepath, store.pendingLocation.pathname, {
2669
- ...opts,
2670
- to: next.pathname
2671
- });
1389
+ });
1390
+ };
1391
+ resolvePath = (from, path) => {
1392
+ return resolvePath(this.basepath, from, cleanPath(path));
1393
+ };
1394
+ navigate = async ({
1395
+ from,
1396
+ to = '.',
1397
+ search,
1398
+ hash,
1399
+ replace,
1400
+ params
1401
+ }) => {
1402
+ // If this link simply reloads the current route,
1403
+ // make sure it has a new key so it will trigger a data refresh
1404
+
1405
+ // If this `to` is a valid external URL, return
1406
+ // null for LinkUtils
1407
+ const toString = String(to);
1408
+ const fromString = String(from);
1409
+ let isExternal;
1410
+ try {
1411
+ new URL(`${toString}`);
1412
+ isExternal = true;
1413
+ } catch (e) {}
1414
+ invariant(!isExternal, 'Attempting to navigate to external url with this.navigate!');
1415
+ return this.#commitLocation({
1416
+ from: fromString,
1417
+ to: toString,
1418
+ search,
1419
+ hash,
1420
+ replace,
1421
+ params
1422
+ });
1423
+ };
1424
+ matchRoute = (location, opts) => {
1425
+ location = {
1426
+ ...location,
1427
+ to: location.to ? this.resolvePath(location.from ?? '', location.to) : undefined
1428
+ };
1429
+ const next = this.buildNext(location);
1430
+ if (opts?.pending) {
1431
+ if (!this.store.state.pendingLocation) {
1432
+ return false;
2672
1433
  }
2673
- return matchPathname(router.basepath, store.currentLocation.pathname, {
1434
+ return matchPathname(this.basepath, this.store.state.pendingLocation.pathname, {
2674
1435
  ...opts,
2675
1436
  to: next.pathname
2676
1437
  });
2677
- },
2678
- navigate: async _ref => {
2679
- let {
2680
- from,
2681
- to = '.',
2682
- search,
2683
- hash,
2684
- replace,
2685
- params
2686
- } = _ref;
2687
- // If this link simply reloads the current route,
2688
- // make sure it has a new key so it will trigger a data refresh
2689
-
2690
- // If this `to` is a valid external URL, return
2691
- // null for LinkUtils
2692
- const toString = String(to);
2693
- const fromString = String(from);
2694
- let isExternal;
2695
- try {
2696
- new URL(`${toString}`);
2697
- isExternal = true;
2698
- } catch (e) {}
2699
- invariant(!isExternal, 'Attempting to navigate to external url with router.navigate!');
2700
- return navigate({
2701
- from: fromString,
2702
- to: toString,
2703
- search,
2704
- hash,
2705
- replace,
2706
- params
2707
- });
2708
- },
2709
- buildLink: _ref2 => {
2710
- let {
2711
- from,
2712
- to = '.',
2713
- search,
2714
- params,
2715
- hash,
2716
- target,
2717
- replace,
2718
- activeOptions,
2719
- preload,
2720
- preloadMaxAge: userPreloadMaxAge,
2721
- preloadGcMaxAge: userPreloadGcMaxAge,
2722
- preloadDelay: userPreloadDelay,
2723
- disabled
2724
- } = _ref2;
2725
- // If this link simply reloads the current route,
2726
- // make sure it has a new key so it will trigger a data refresh
2727
-
2728
- // If this `to` is a valid external URL, return
2729
- // null for LinkUtils
1438
+ }
1439
+ return matchPathname(this.basepath, this.store.state.currentLocation.pathname, {
1440
+ ...opts,
1441
+ to: next.pathname
1442
+ });
1443
+ };
1444
+ buildLink = ({
1445
+ from,
1446
+ to = '.',
1447
+ search,
1448
+ params,
1449
+ hash,
1450
+ target,
1451
+ replace,
1452
+ activeOptions,
1453
+ preload,
1454
+ preloadMaxAge: userPreloadMaxAge,
1455
+ preloadGcMaxAge: userPreloadGcMaxAge,
1456
+ preloadDelay: userPreloadDelay,
1457
+ disabled
1458
+ }) => {
1459
+ // If this link simply reloads the current route,
1460
+ // make sure it has a new key so it will trigger a data refresh
2730
1461
 
2731
- try {
2732
- new URL(`${to}`);
2733
- return {
2734
- type: 'external',
2735
- href: to
2736
- };
2737
- } catch (e) {}
2738
- const nextOpts = {
2739
- from,
2740
- to,
2741
- search,
2742
- params,
2743
- hash,
2744
- replace
2745
- };
2746
- const next = router.buildNext(nextOpts);
2747
- preload = preload ?? router.options.defaultPreload;
2748
- const preloadDelay = userPreloadDelay ?? router.options.defaultPreloadDelay ?? 0;
2749
-
2750
- // Compare path/hash for matches
2751
- const pathIsEqual = store.currentLocation.pathname === next.pathname;
2752
- const currentPathSplit = store.currentLocation.pathname.split('/');
2753
- const nextPathSplit = next.pathname.split('/');
2754
- const pathIsFuzzyEqual = nextPathSplit.every((d, i) => d === currentPathSplit[i]);
2755
- const hashIsEqual = store.currentLocation.hash === next.hash;
2756
- // Combine the matches based on user options
2757
- const pathTest = activeOptions != null && activeOptions.exact ? pathIsEqual : pathIsFuzzyEqual;
2758
- const hashTest = activeOptions != null && activeOptions.includeHash ? hashIsEqual : true;
2759
-
2760
- // The final "active" test
2761
- const isActive = pathTest && hashTest;
2762
-
2763
- // The click handler
2764
- const handleClick = e => {
2765
- if (!disabled && !isCtrlEvent(e) && !e.defaultPrevented && (!target || target === '_self') && e.button === 0) {
2766
- e.preventDefault();
2767
- if (pathIsEqual && !search && !hash) {
2768
- router.invalidateRoute(nextOpts);
2769
- }
1462
+ // If this `to` is a valid external URL, return
1463
+ // null for LinkUtils
2770
1464
 
2771
- // All is well? Navigate!
2772
- navigate(nextOpts);
2773
- }
1465
+ try {
1466
+ new URL(`${to}`);
1467
+ return {
1468
+ type: 'external',
1469
+ href: to
2774
1470
  };
1471
+ } catch (e) {}
1472
+ const nextOpts = {
1473
+ from,
1474
+ to,
1475
+ search,
1476
+ params,
1477
+ hash,
1478
+ replace
1479
+ };
1480
+ const next = this.buildNext(nextOpts);
1481
+ preload = preload ?? this.options.defaultPreload;
1482
+ const preloadDelay = userPreloadDelay ?? this.options.defaultPreloadDelay ?? 0;
1483
+
1484
+ // Compare path/hash for matches
1485
+ const pathIsEqual = this.store.state.currentLocation.pathname === next.pathname;
1486
+ const currentPathSplit = this.store.state.currentLocation.pathname.split('/');
1487
+ const nextPathSplit = next.pathname.split('/');
1488
+ const pathIsFuzzyEqual = nextPathSplit.every((d, i) => d === currentPathSplit[i]);
1489
+ const hashIsEqual = this.store.state.currentLocation.hash === next.hash;
1490
+ // Combine the matches based on user options
1491
+ const pathTest = activeOptions?.exact ? pathIsEqual : pathIsFuzzyEqual;
1492
+ const hashTest = activeOptions?.includeHash ? hashIsEqual : true;
1493
+
1494
+ // The final "active" test
1495
+ const isActive = pathTest && hashTest;
1496
+
1497
+ // The click handler
1498
+ const handleClick = e => {
1499
+ if (!disabled && !isCtrlEvent(e) && !e.defaultPrevented && (!target || target === '_self') && e.button === 0) {
1500
+ e.preventDefault();
1501
+ if (pathIsEqual && !search && !hash) {
1502
+ this.invalidateRoute(nextOpts);
1503
+ }
1504
+
1505
+ // All is well? Navigate!
1506
+ this.#commitLocation(nextOpts);
1507
+ }
1508
+ };
2775
1509
 
2776
- // The click handler
2777
- const handleFocus = e => {
2778
- if (preload) {
2779
- router.preloadRoute(nextOpts, {
1510
+ // The click handler
1511
+ const handleFocus = e => {
1512
+ if (preload) {
1513
+ this.preloadRoute(nextOpts, {
1514
+ maxAge: userPreloadMaxAge,
1515
+ gcMaxAge: userPreloadGcMaxAge
1516
+ }).catch(err => {
1517
+ console.warn(err);
1518
+ console.warn('Error preloading route! ☝️');
1519
+ });
1520
+ }
1521
+ };
1522
+ const handleEnter = e => {
1523
+ const target = e.target || {};
1524
+ if (preload) {
1525
+ if (target.preloadTimeout) {
1526
+ return;
1527
+ }
1528
+ target.preloadTimeout = setTimeout(() => {
1529
+ target.preloadTimeout = null;
1530
+ this.preloadRoute(nextOpts, {
2780
1531
  maxAge: userPreloadMaxAge,
2781
1532
  gcMaxAge: userPreloadGcMaxAge
2782
1533
  }).catch(err => {
2783
- console.log(err);
1534
+ console.warn(err);
2784
1535
  console.warn('Error preloading route! ☝️');
2785
1536
  });
2786
- }
2787
- };
2788
- const handleEnter = e => {
2789
- const target = e.target || {};
2790
- if (preload) {
2791
- if (target.preloadTimeout) {
2792
- return;
1537
+ }, preloadDelay);
1538
+ }
1539
+ };
1540
+ const handleLeave = e => {
1541
+ const target = e.target || {};
1542
+ if (target.preloadTimeout) {
1543
+ clearTimeout(target.preloadTimeout);
1544
+ target.preloadTimeout = null;
1545
+ }
1546
+ };
1547
+ return {
1548
+ type: 'internal',
1549
+ next,
1550
+ handleFocus,
1551
+ handleClick,
1552
+ handleEnter,
1553
+ handleLeave,
1554
+ isActive,
1555
+ disabled
1556
+ };
1557
+ };
1558
+ dehydrate = () => {
1559
+ return {
1560
+ state: {
1561
+ ...pick(this.store.state, ['latestLocation', 'currentLocation', 'status', 'lastUpdated']),
1562
+ currentMatches: this.store.state.currentMatches.map(match => ({
1563
+ matchId: match.id,
1564
+ state: {
1565
+ ...pick(match.store.state, ['status', 'routeLoaderData', 'invalidAt', 'invalid'])
2793
1566
  }
2794
- target.preloadTimeout = setTimeout(() => {
2795
- target.preloadTimeout = null;
2796
- router.preloadRoute(nextOpts, {
2797
- maxAge: userPreloadMaxAge,
2798
- gcMaxAge: userPreloadGcMaxAge
2799
- }).catch(err => {
2800
- console.log(err);
2801
- console.warn('Error preloading route! ☝️');
1567
+ }))
1568
+ },
1569
+ context: this.options.context
1570
+ };
1571
+ };
1572
+ hydrate = dehydratedRouter => {
1573
+ this.store.setState(s => {
1574
+ // Update the context TODO: make this part of state?
1575
+ this.options.context = dehydratedRouter.context;
1576
+
1577
+ // Match the routes
1578
+ const currentMatches = this.matchRoutes(dehydratedRouter.state.latestLocation.pathname, {
1579
+ strictParseParams: true
1580
+ });
1581
+ currentMatches.forEach((match, index) => {
1582
+ const dehydratedMatch = dehydratedRouter.state.currentMatches[index];
1583
+ invariant(dehydratedMatch && dehydratedMatch.matchId === match.id, 'Oh no! There was a hydration mismatch when attempting to rethis.store the state of the router! 😬');
1584
+ Object.assign(match, dehydratedMatch);
1585
+ });
1586
+ currentMatches.forEach(match => match.__validate());
1587
+ Object.assign(s, {
1588
+ ...dehydratedRouter.state,
1589
+ currentMatches
1590
+ });
1591
+ });
1592
+ };
1593
+ getLoader = opts => {
1594
+ const id = opts.from || '/';
1595
+ const route = this.getRoute(id);
1596
+ if (!route) return undefined;
1597
+ let loader = this.store.state.loaders[id] || (() => {
1598
+ this.store.setState(s => {
1599
+ s.loaders[id] = {
1600
+ pending: [],
1601
+ fetch: async loaderContext => {
1602
+ if (!route) {
1603
+ return;
1604
+ }
1605
+ const loaderState = {
1606
+ loadedAt: Date.now(),
1607
+ loaderContext
1608
+ };
1609
+ this.store.setState(s => {
1610
+ s.loaders[id].current = loaderState;
1611
+ s.loaders[id].latest = loaderState;
1612
+ s.loaders[id].pending.push(loaderState);
2802
1613
  });
2803
- }, preloadDelay);
2804
- }
2805
- };
2806
- const handleLeave = e => {
2807
- const target = e.target || {};
2808
- if (target.preloadTimeout) {
2809
- clearTimeout(target.preloadTimeout);
2810
- target.preloadTimeout = null;
1614
+ try {
1615
+ return await route.options.loader?.(loaderContext);
1616
+ } finally {
1617
+ this.store.setState(s => {
1618
+ s.loaders[id].pending = s.loaders[id].pending.filter(d => d !== loaderState);
1619
+ });
1620
+ }
1621
+ }
1622
+ };
1623
+ });
1624
+ return this.store.state.loaders[id];
1625
+ })();
1626
+ return loader;
1627
+ };
1628
+ #buildRouteTree = rootRouteConfig => {
1629
+ const recurseRoutes = (routeConfigs, parent) => {
1630
+ return routeConfigs.map((routeConfig, i) => {
1631
+ const routeOptions = routeConfig.options;
1632
+ const route = new Route(routeConfig, routeOptions, i, parent, this);
1633
+ const existingRoute = this.routesById[route.id];
1634
+ if (existingRoute) {
1635
+ {
1636
+ console.warn(`Duplicate routes found with id: ${String(route.id)}`, this.routesById, route);
1637
+ }
1638
+ throw new Error();
2811
1639
  }
2812
- };
2813
- return {
2814
- type: 'internal',
2815
- next,
2816
- handleFocus,
2817
- handleClick,
2818
- handleEnter,
2819
- handleLeave,
2820
- isActive,
2821
- disabled
2822
- };
2823
- },
2824
- buildNext: opts => {
2825
- const next = buildLocation(opts);
2826
- const matches = router.matchRoutes(next.pathname);
2827
- const __preSearchFilters = matches.map(match => match.options.preSearchFilters ?? []).flat().filter(Boolean);
2828
- const __postSearchFilters = matches.map(match => match.options.postSearchFilters ?? []).flat().filter(Boolean);
2829
- return buildLocation({
2830
- ...opts,
2831
- __preSearchFilters,
2832
- __postSearchFilters
1640
+ this.routesById[route.id] = route;
1641
+ const children = routeConfig.children;
1642
+ route.childRoutes = children.length ? recurseRoutes(children, route) : undefined;
1643
+ return route;
1644
+ });
1645
+ };
1646
+ const routes = recurseRoutes([rootRouteConfig]);
1647
+ return routes[0];
1648
+ };
1649
+ #parseLocation = previousLocation => {
1650
+ let {
1651
+ pathname,
1652
+ search,
1653
+ hash,
1654
+ state
1655
+ } = this.history.location;
1656
+ const parsedSearch = this.options.parseSearch(search);
1657
+ console.log({
1658
+ pathname: pathname,
1659
+ searchStr: search,
1660
+ search: replaceEqualDeep(previousLocation?.search, parsedSearch),
1661
+ hash: hash.split('#').reverse()[0] ?? '',
1662
+ href: `${pathname}${search}${hash}`,
1663
+ state: state,
1664
+ key: state?.key || '__init__'
1665
+ });
1666
+ return {
1667
+ pathname: pathname,
1668
+ searchStr: search,
1669
+ search: replaceEqualDeep(previousLocation?.search, parsedSearch),
1670
+ hash: hash.split('#').reverse()[0] ?? '',
1671
+ href: `${pathname}${search}${hash}`,
1672
+ state: state,
1673
+ key: state?.key || '__init__'
1674
+ };
1675
+ };
1676
+ #onFocus = () => {
1677
+ this.load();
1678
+ };
1679
+ #buildLocation = (dest = {}) => {
1680
+ const fromPathname = dest.fromCurrent ? this.store.state.latestLocation.pathname : dest.from ?? this.store.state.latestLocation.pathname;
1681
+ let pathname = resolvePath(this.basepath ?? '/', fromPathname, `${dest.to ?? '.'}`);
1682
+ const fromMatches = this.matchRoutes(this.store.state.latestLocation.pathname, {
1683
+ strictParseParams: true
1684
+ });
1685
+ const toMatches = this.matchRoutes(pathname);
1686
+ const prevParams = {
1687
+ ...last(fromMatches)?.params
1688
+ };
1689
+ let nextParams = (dest.params ?? true) === true ? prevParams : functionalUpdate(dest.params, prevParams);
1690
+ if (nextParams) {
1691
+ toMatches.map(d => d.route.options.stringifyParams).filter(Boolean).forEach(fn => {
1692
+ Object.assign({}, nextParams, fn(nextParams));
2833
1693
  });
2834
1694
  }
1695
+ pathname = interpolatePath(pathname, nextParams ?? {});
1696
+
1697
+ // Pre filters first
1698
+ const preFilteredSearch = dest.__preSearchFilters?.length ? dest.__preSearchFilters?.reduce((prev, next) => next(prev), this.store.state.latestLocation.search) : this.store.state.latestLocation.search;
1699
+
1700
+ // Then the link/navigate function
1701
+ const destSearch = dest.search === true ? preFilteredSearch // Preserve resolvedFrom true
1702
+ : dest.search ? functionalUpdate(dest.search, preFilteredSearch) ?? {} // Updater
1703
+ : dest.__preSearchFilters?.length ? preFilteredSearch // Preserve resolvedFrom filters
1704
+ : {};
1705
+
1706
+ // Then post filters
1707
+ const postFilteredSearch = dest.__postSearchFilters?.length ? dest.__postSearchFilters.reduce((prev, next) => next(prev), destSearch) : destSearch;
1708
+ const search = replaceEqualDeep(this.store.state.latestLocation.search, postFilteredSearch);
1709
+ const searchStr = this.options.stringifySearch(search);
1710
+ let hash = dest.hash === true ? this.store.state.latestLocation.hash : functionalUpdate(dest.hash, this.store.state.latestLocation.hash);
1711
+ hash = hash ? `#${hash}` : '';
1712
+ return {
1713
+ pathname,
1714
+ search,
1715
+ searchStr,
1716
+ state: this.store.state.latestLocation.state,
1717
+ hash,
1718
+ href: `${pathname}${searchStr}${hash}`,
1719
+ key: dest.key
1720
+ };
1721
+ };
1722
+ #commitLocation = location => {
1723
+ const next = this.buildNext(location);
1724
+ const id = '' + Date.now() + Math.random();
1725
+ if (this.navigateTimeout) clearTimeout(this.navigateTimeout);
1726
+ let nextAction = 'replace';
1727
+ if (!location.replace) {
1728
+ nextAction = 'push';
1729
+ }
1730
+ const isSameUrl = this.store.state.latestLocation.href === next.href;
1731
+ if (isSameUrl && !next.key) {
1732
+ nextAction = 'replace';
1733
+ }
1734
+ const href = `${next.pathname}${next.searchStr}${next.hash ? `#${next.hash}` : ''}`;
1735
+ this.history[nextAction === 'push' ? 'push' : 'replace'](href, {
1736
+ id,
1737
+ ...next.state
1738
+ });
1739
+ this.load(this.#parseLocation(this.store.state.latestLocation));
1740
+ return this.navigationPromise = new Promise(resolve => {
1741
+ const previousNavigationResolve = this.resolveNavigation;
1742
+ this.resolveNavigation = () => {
1743
+ previousNavigationResolve();
1744
+ resolve();
1745
+ };
1746
+ });
2835
1747
  };
2836
- router.update(userOptions);
1748
+ }
2837
1749
 
2838
- // Allow frameworks to hook into the router creation
2839
- router.options.createRouter == null ? void 0 : router.options.createRouter(router);
2840
- return router;
1750
+ // Detect if we're in the DOM
1751
+ const isServer = typeof window === 'undefined' || !window.document.createElement;
1752
+ function getInitialRouterState() {
1753
+ return {
1754
+ status: 'idle',
1755
+ latestLocation: null,
1756
+ currentLocation: null,
1757
+ currentMatches: [],
1758
+ loaders: {},
1759
+ lastUpdated: Date.now(),
1760
+ matchCache: {},
1761
+ get isFetching() {
1762
+ return this.status === 'loading' || this.currentMatches.some(d => d.store.state.isFetching);
1763
+ },
1764
+ get isPreloading() {
1765
+ return Object.values(this.matchCache).some(d => d.match.store.state.isFetching && !this.currentMatches.find(dd => dd.id === d.match.id));
1766
+ }
1767
+ };
2841
1768
  }
2842
1769
  function isCtrlEvent(e) {
2843
1770
  return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
@@ -2846,13 +1773,130 @@
2846
1773
  matches.forEach((match, index) => {
2847
1774
  const parent = matches[index - 1];
2848
1775
  if (parent) {
2849
- match.__.setParentMatch(parent);
2850
- } else {
2851
- match.__.setParentMatch(undefined);
1776
+ match.__setParentMatch(parent);
2852
1777
  }
2853
1778
  });
2854
1779
  }
2855
1780
 
1781
+ // createRouterAction is a constrained identify function that takes options: key, action, onSuccess, onError, onSettled, etc
1782
+ function createAction(options) {
1783
+ const store = createStore({
1784
+ submissions: []
1785
+ }, options.debug);
1786
+ return {
1787
+ options,
1788
+ store,
1789
+ reset: () => {
1790
+ store.setState(s => {
1791
+ s.submissions = [];
1792
+ });
1793
+ },
1794
+ submit: async payload => {
1795
+ const submission = {
1796
+ submittedAt: Date.now(),
1797
+ status: 'pending',
1798
+ payload: payload,
1799
+ invalidate: () => {
1800
+ setSubmission(s => {
1801
+ s.isInvalid = true;
1802
+ });
1803
+ },
1804
+ getIsLatest: () => store.state.submissions[store.state.submissions.length - 1]?.submittedAt === submission.submittedAt
1805
+ };
1806
+ const setSubmission = updater => {
1807
+ store.setState(s => {
1808
+ const a = s.submissions.find(d => d.submittedAt === submission.submittedAt);
1809
+ invariant(a, 'Could not find submission in store');
1810
+ updater(a);
1811
+ });
1812
+ };
1813
+ store.setState(s => {
1814
+ s.submissions.push(submission);
1815
+ s.submissions.reverse();
1816
+ s.submissions = s.submissions.slice(0, options.maxSubmissions ?? 10);
1817
+ s.submissions.reverse();
1818
+ });
1819
+ const after = async () => {
1820
+ options.onEachSettled?.(submission);
1821
+ if (submission.getIsLatest()) await options.onLatestSettled?.(submission);
1822
+ };
1823
+ try {
1824
+ const res = await options.action?.(submission.payload);
1825
+ setSubmission(s => {
1826
+ s.response = res;
1827
+ });
1828
+ await options.onEachSuccess?.(submission);
1829
+ if (submission.getIsLatest()) await options.onLatestSuccess?.(submission);
1830
+ await after();
1831
+ setSubmission(s => {
1832
+ s.status = 'success';
1833
+ });
1834
+ return res;
1835
+ } catch (err) {
1836
+ console.error(err);
1837
+ setSubmission(s => {
1838
+ s.error = err;
1839
+ });
1840
+ await options.onEachError?.(submission);
1841
+ if (submission.getIsLatest()) await options.onLatestError?.(submission);
1842
+ await after();
1843
+ setSubmission(s => {
1844
+ s.status = 'error';
1845
+ });
1846
+ throw err;
1847
+ }
1848
+ }
1849
+ };
1850
+ }
1851
+
1852
+ function useStore(store, selector = d => d, compareShallow) {
1853
+ const slice = withSelector.useSyncExternalStoreWithSelector(store.subscribe, () => store.state, () => store.state, selector, compareShallow ? shallow : undefined);
1854
+ return slice;
1855
+ }
1856
+ function shallow(objA, objB) {
1857
+ if (Object.is(objA, objB)) {
1858
+ return true;
1859
+ }
1860
+ if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
1861
+ return false;
1862
+ }
1863
+
1864
+ // if (objA instanceof Map && objB instanceof Map) {
1865
+ // if (objA.size !== objB.size) return false
1866
+
1867
+ // for (const [key, value] of objA) {
1868
+ // if (!Object.is(value, objB.get(key))) {
1869
+ // return false
1870
+ // }
1871
+ // }
1872
+ // return true
1873
+ // }
1874
+
1875
+ // if (objA instanceof Set && objB instanceof Set) {
1876
+ // if (objA.size !== objB.size) return false
1877
+
1878
+ // for (const value of objA) {
1879
+ // if (!objB.has(value)) {
1880
+ // return false
1881
+ // }
1882
+ // }
1883
+ // return true
1884
+ // }
1885
+
1886
+ const keysA = Object.keys(objA);
1887
+ if (keysA.length !== Object.keys(objB).length) {
1888
+ return false;
1889
+ }
1890
+ for (let i = 0; i < keysA.length; i++) {
1891
+ if (!Object.prototype.hasOwnProperty.call(objB, keysA[i]) || !Object.is(objA[keysA[i]], objB[keysA[i]])) {
1892
+ return false;
1893
+ }
1894
+ }
1895
+ return true;
1896
+ }
1897
+
1898
+ //
1899
+
2856
1900
  function lazy(importer) {
2857
1901
  const lazyComp = /*#__PURE__*/React__namespace.lazy(importer);
2858
1902
  const finalComp = lazyComp;
@@ -2882,7 +1926,7 @@
2882
1926
  hash,
2883
1927
  search,
2884
1928
  params,
2885
- to,
1929
+ to = '.',
2886
1930
  preload,
2887
1931
  preloadDelay,
2888
1932
  preloadMaxAge,
@@ -2963,7 +2007,7 @@
2963
2007
  }
2964
2008
  const Link = /*#__PURE__*/React__namespace.forwardRef((props, ref) => {
2965
2009
  const linkProps = useLinkProps(props);
2966
- return /*#__PURE__*/React__namespace.createElement("a", _extends$1({
2010
+ return /*#__PURE__*/React__namespace.createElement("a", _extends({
2967
2011
  ref: ref
2968
2012
  }, linkProps, {
2969
2013
  children: typeof props.children === 'function' ? props.children({
@@ -2973,82 +2017,26 @@
2973
2017
  });
2974
2018
  const matchesContext = /*#__PURE__*/React__namespace.createContext(null);
2975
2019
  const routerContext = /*#__PURE__*/React__namespace.createContext(null);
2976
- const EMPTY = {};
2977
- const __useStoreValue = (seed, selector) => {
2978
- const valueRef = React__namespace.useRef(EMPTY);
2979
-
2980
- // If there is no selector, track the seed
2981
- // If there is a selector, do not track the seed
2982
- const getValue = () => !selector ? seed() : selector(untrack(() => seed()));
2983
-
2984
- // If empty, initialize the value
2985
- if (valueRef.current === EMPTY) {
2986
- valueRef.current = sharedClone(undefined, getValue());
2987
- }
2988
-
2989
- // Snapshot should just return the current cached value
2990
- const getSnapshot = React__namespace.useCallback(() => valueRef.current, []);
2991
- const getStore = React__namespace.useCallback(cb => {
2992
- // A root is necessary to track effects
2993
- return createRoot(() => {
2994
- createEffect(() => {
2995
- // Read and update the value
2996
- // getValue will handle which values are accessed and
2997
- // thus tracked.
2998
- // sharedClone will both recursively track the end result
2999
- // and ensure that the previous value is structurally shared
3000
- // into the new version.
3001
- valueRef.current = unwrap(
3002
- // Unwrap the value to get rid of any proxy structures
3003
- sharedClone(valueRef.current, getValue()));
3004
- cb();
3005
- });
2020
+ class ReactRouter extends Router {
2021
+ constructor(opts) {
2022
+ super({
2023
+ ...opts,
2024
+ loadComponent: async component => {
2025
+ if (component.preload) {
2026
+ await component.preload();
2027
+ }
2028
+ return component;
2029
+ }
3006
2030
  });
3007
- }, []);
3008
- return shim.useSyncExternalStore(getStore, getSnapshot, getSnapshot);
3009
- };
3010
- const [store, setStore] = createStore({
3011
- foo: 'foo',
3012
- bar: {
3013
- baz: 'baz'
3014
2031
  }
3015
- });
3016
- createRoot(() => {
3017
- let prev;
3018
- createEffect(() => {
3019
- console.log('effect');
3020
- const next = sharedClone(prev, store);
3021
- console.log(next);
3022
- prev = untrack(() => next);
3023
- });
3024
- });
3025
- setStore(s => {
3026
- s.foo = '1';
3027
- });
3028
- setStore(s => {
3029
- s.bar.baz = '2';
3030
- });
3031
- function createReactRouter(opts) {
3032
- const coreRouter = createRouter({
3033
- ...opts,
3034
- loadComponent: async component => {
3035
- if (component.preload) {
3036
- await component.preload();
3037
- }
3038
- return component;
3039
- }
3040
- });
3041
- return coreRouter;
3042
2032
  }
3043
- function RouterProvider(_ref) {
3044
- let {
3045
- router,
3046
- ...rest
3047
- } = _ref;
2033
+ function RouterProvider({
2034
+ router,
2035
+ ...rest
2036
+ }) {
3048
2037
  router.update(rest);
3049
- const [,, currentMatches] = __useStoreValue(() => router.store, s => [s.status, s.pendingMatches, s.currentMatches]);
2038
+ const [,, currentMatches] = useStore(router.store, s => [s.status, s.pendingMatches, s.currentMatches], true);
3050
2039
  React__namespace.useEffect(router.mount, [router]);
3051
- console.log('current', currentMatches);
3052
2040
  return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(routerContext.Provider, {
3053
2041
  value: {
3054
2042
  router: router
@@ -3062,9 +2050,9 @@
3062
2050
  warning(!value, 'useRouter must be used inside a <Router> component!');
3063
2051
  return value.router;
3064
2052
  }
3065
- function useRouterStore(selector) {
2053
+ function useRouterStore(selector, shallow) {
3066
2054
  const router = useRouter();
3067
- return __useStoreValue(() => router.store, selector);
2055
+ return useStore(router.store, selector, shallow);
3068
2056
  }
3069
2057
  function useMatches() {
3070
2058
  return React__namespace.useContext(matchesContext);
@@ -3072,12 +2060,12 @@
3072
2060
  function useMatch(opts) {
3073
2061
  const router = useRouter();
3074
2062
  const nearestMatch = useMatches()[0];
3075
- const match = opts != null && opts.from ? router.store.currentMatches.find(d => d.routeId === (opts == null ? void 0 : opts.from)) : nearestMatch;
3076
- invariant(match, `Could not find ${opts != null && opts.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`);
3077
- if ((opts == null ? void 0 : opts.strict) ?? true) {
3078
- invariant(nearestMatch.routeId == (match == null ? void 0 : match.routeId), `useMatch("${match == null ? void 0 : match.routeId}") is being called in a component that is meant to render the '${nearestMatch.routeId}' route. Did you mean to 'useMatch("${match == null ? void 0 : match.routeId}", { strict: false })' or 'useRoute("${match == null ? void 0 : match.routeId}")' instead?`);
2063
+ const match = opts?.from ? router.store.state.currentMatches.find(d => d.route.id === opts?.from) : nearestMatch;
2064
+ invariant(match, `Could not find ${opts?.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`);
2065
+ if (opts?.strict ?? true) {
2066
+ invariant(nearestMatch.route.id == match?.route.id, `useMatch("${match?.route.id}") is being called in a component that is meant to render the '${nearestMatch.route.id}' route. Did you mean to 'useMatch("${match?.route.id}", { strict: false })' or 'useRoute("${match?.route.id}")' instead?`);
3079
2067
  }
3080
- __useStoreValue(() => match.store);
2068
+ useStore(match.store, d => opts?.track?.(match) ?? match, opts?.shallow);
3081
2069
  return match;
3082
2070
  }
3083
2071
  function useRoute(routeId) {
@@ -3088,34 +2076,32 @@
3088
2076
  }
3089
2077
  function useLoaderData(opts) {
3090
2078
  const match = useMatch(opts);
3091
- return __useStoreValue(() => match == null ? void 0 : match.store.loaderData, opts == null ? void 0 : opts.select);
2079
+ invariant(match, `Could not find ${opts?.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`);
2080
+ useStore(match.store, d => opts?.track?.(d.loaderData) ?? d.loaderData);
2081
+ return match.store.state.loaderData;
3092
2082
  }
3093
2083
  function useSearch(opts) {
3094
2084
  const match = useMatch(opts);
3095
- return __useStoreValue(() => match == null ? void 0 : match.store.search, opts == null ? void 0 : opts.select);
2085
+ useStore(match.store, d => opts?.track?.(d.search) ?? d.search);
2086
+ return match.store.state.search;
3096
2087
  }
3097
2088
  function useParams(opts) {
3098
2089
  const router = useRouter();
3099
- return __useStoreValue(() => {
3100
- var _last;
3101
- return (_last = last(router.store.currentMatches)) == null ? void 0 : _last.params;
3102
- }, opts == null ? void 0 : opts.select);
2090
+ useStore(router.store, d => {
2091
+ const params = last(d.currentMatches)?.params;
2092
+ return opts?.track?.(params) ?? params;
2093
+ });
2094
+ return last(router.store.state.currentMatches)?.params;
3103
2095
  }
3104
2096
  function useNavigate(defaultOpts) {
2097
+ const router = useRouter();
3105
2098
  return opts => {
3106
- const router = useRouter();
3107
2099
  return router.navigate({
3108
2100
  ...defaultOpts,
3109
2101
  ...opts
3110
2102
  });
3111
2103
  };
3112
2104
  }
3113
- function useAction(opts) {
3114
- const route = useRoute(opts.from);
3115
- const action = route.action;
3116
- __useStoreValue(() => action);
3117
- return action;
3118
- }
3119
2105
  function useMatchRoute() {
3120
2106
  const router = useRouter();
3121
2107
  return opts => {
@@ -3136,37 +2122,49 @@
3136
2122
  if (!params) {
3137
2123
  return null;
3138
2124
  }
3139
- return /*#__PURE__*/React__namespace.createElement(typeof props.children === 'function' ? props.children(params) : props.children, props);
2125
+ if (typeof props.children === 'function') {
2126
+ return props.children(params);
2127
+ }
2128
+ return params ? props.children : null;
3140
2129
  }
3141
2130
  function Outlet() {
3142
- const router = useRouter();
3143
2131
  const matches = useMatches().slice(1);
3144
2132
  const match = matches[0];
2133
+ if (!match) {
2134
+ return null;
2135
+ }
2136
+ return /*#__PURE__*/React__namespace.createElement(SubOutlet, {
2137
+ matches: matches,
2138
+ match: match
2139
+ });
2140
+ }
2141
+ function SubOutlet({
2142
+ matches,
2143
+ match
2144
+ }) {
2145
+ const router = useRouter();
2146
+ useStore(match.store);
3145
2147
  const defaultPending = React__namespace.useCallback(() => null, []);
3146
- __useStoreValue(() => match == null ? void 0 : match.store);
3147
2148
  const Inner = React__namespace.useCallback(props => {
3148
- if (props.match.store.status === 'error') {
3149
- throw props.match.store.error;
2149
+ if (props.match.store.state.status === 'error') {
2150
+ throw props.match.store.state.error;
3150
2151
  }
3151
- if (props.match.store.status === 'success') {
3152
- return /*#__PURE__*/React__namespace.createElement(props.match.__.component ?? router.options.defaultComponent ?? Outlet);
2152
+ if (props.match.store.state.status === 'success') {
2153
+ return /*#__PURE__*/React__namespace.createElement(props.match.component ?? router.options.defaultComponent ?? Outlet);
3153
2154
  }
3154
- if (props.match.store.status === 'loading') {
3155
- throw props.match.__.loadPromise;
2155
+ if (props.match.store.state.status === 'loading') {
2156
+ throw props.match.__loadPromise;
3156
2157
  }
3157
2158
  invariant(false, 'Idle routeMatch status encountered during rendering! You should never see this. File an issue!');
3158
2159
  }, []);
3159
- if (!match) {
3160
- return null;
3161
- }
3162
- const PendingComponent = match.__.pendingComponent ?? router.options.defaultPendingComponent ?? defaultPending;
3163
- const errorComponent = match.__.errorComponent ?? router.options.defaultErrorComponent;
2160
+ const PendingComponent = match.pendingComponent ?? router.options.defaultPendingComponent ?? defaultPending;
2161
+ const errorComponent = match.errorComponent ?? router.options.defaultErrorComponent;
3164
2162
  return /*#__PURE__*/React__namespace.createElement(matchesContext.Provider, {
3165
2163
  value: matches
3166
2164
  }, /*#__PURE__*/React__namespace.createElement(React__namespace.Suspense, {
3167
2165
  fallback: /*#__PURE__*/React__namespace.createElement(PendingComponent, null)
3168
2166
  }, /*#__PURE__*/React__namespace.createElement(CatchBoundary, {
3169
- key: match.routeId,
2167
+ key: match.route.id,
3170
2168
  errorComponent: errorComponent,
3171
2169
  match: match
3172
2170
  }, /*#__PURE__*/React__namespace.createElement(Inner, {
@@ -3179,7 +2177,7 @@
3179
2177
  info: undefined
3180
2178
  };
3181
2179
  componentDidCatch(error, info) {
3182
- console.error(`Error in route match: ${this.props.match.matchId}`);
2180
+ console.error(`Error in route match: ${this.props.match.id}`);
3183
2181
  console.error(error);
3184
2182
  this.setState({
3185
2183
  error,
@@ -3187,7 +2185,7 @@
3187
2185
  });
3188
2186
  }
3189
2187
  render() {
3190
- return /*#__PURE__*/React__namespace.createElement(CatchBoundaryInner, _extends$1({}, this.props, {
2188
+ return /*#__PURE__*/React__namespace.createElement(CatchBoundaryInner, _extends({}, this.props, {
3191
2189
  errorState: this.state,
3192
2190
  reset: () => this.setState({})
3193
2191
  }));
@@ -3199,20 +2197,27 @@
3199
2197
  // router's location key changes.
3200
2198
  function CatchBoundaryInner(props) {
3201
2199
  const [activeErrorState, setActiveErrorState] = React__namespace.useState(props.errorState);
3202
- const router = useRouter();
2200
+ useRouter();
3203
2201
  const errorComponent = props.errorComponent ?? DefaultErrorBoundary;
3204
- React__namespace.useEffect(() => {
3205
- if (activeErrorState) {
3206
- let prevKey = router.store.currentLocation.key;
3207
- return createRoot(() => createEffect(() => {
3208
- if (router.store.currentLocation.key !== prevKey) {
3209
- prevKey = router.store.currentLocation.key;
3210
- setActiveErrorState({});
3211
- }
3212
- }));
3213
- }
3214
- return;
3215
- }, [activeErrorState]);
2202
+
2203
+ // React.useEffect(() => {
2204
+ // if (activeErrorState) {
2205
+ // let prevKey = router.store.currentLocation.key
2206
+ // return createRoot((dispose) => {
2207
+ // createEffect(() => {
2208
+ // if (router.store.currentLocation.key !== prevKey) {
2209
+ // prevKey = router.store.currentLocation.key
2210
+ // setActiveErrorState({} as any)
2211
+ // }
2212
+ // })
2213
+
2214
+ // return dispose
2215
+ // })
2216
+ // }
2217
+
2218
+ // return
2219
+ // }, [activeErrorState])
2220
+
3216
2221
  React__namespace.useEffect(() => {
3217
2222
  if (props.errorState.error) {
3218
2223
  setActiveErrorState(props.errorState);
@@ -3224,10 +2229,9 @@
3224
2229
  }
3225
2230
  return props.children;
3226
2231
  }
3227
- function DefaultErrorBoundary(_ref2) {
3228
- let {
3229
- error
3230
- } = _ref2;
2232
+ function DefaultErrorBoundary({
2233
+ error
2234
+ }) {
3231
2235
  return /*#__PURE__*/React__namespace.createElement("div", {
3232
2236
  style: {
3233
2237
  padding: '.5rem',
@@ -3251,98 +2255,87 @@
3251
2255
  }
3252
2256
  }, error.message) : null)));
3253
2257
  }
3254
- function usePrompt(message, when) {
3255
- const router = useRouter();
3256
- React__namespace.useEffect(() => {
3257
- if (!when) return;
3258
- let unblock = router.history.block(transition => {
3259
- if (window.confirm(message)) {
3260
- unblock();
3261
- transition.retry();
3262
- } else {
3263
- router.store.currentLocation.pathname = window.location.pathname;
3264
- }
3265
- });
3266
- return unblock;
3267
- }, [when, message]);
3268
- }
3269
- function Prompt(_ref3) {
3270
- let {
3271
- message,
3272
- when,
3273
- children
3274
- } = _ref3;
3275
- usePrompt(message, when ?? true);
3276
- return children ?? null;
3277
- }
3278
-
3279
- // function circularStringify(obj: any) {
3280
- // const seen = new Set()
3281
-
3282
- // return (
3283
- // JSON.stringify(obj, (_, value) => {
3284
- // if (typeof value === 'function') {
3285
- // return undefined
3286
- // }
3287
- // if (typeof value === 'object' && value !== null) {
3288
- // if (seen.has(value)) return
3289
- // seen.add(value)
2258
+ function useAction(action, opts) {
2259
+ useStore(action.store, d => opts?.track?.(d) ?? d, true);
2260
+ const [ref] = React__namespace.useState({});
2261
+ Object.assign(ref, {
2262
+ ...action,
2263
+ latestSubmission: action.store.state.submissions[action.store.state.submissions.length - 1],
2264
+ pendingSubmissions: React__namespace.useMemo(() => action.store.state.submissions.filter(d => d.status === 'pending'), [action.store.state.submissions])
2265
+ });
2266
+ return ref;
2267
+ }
2268
+
2269
+ // TODO: While we migrate away from the history package, these need to be disabled
2270
+ // export function usePrompt(message: string, when: boolean | any): void {
2271
+ // const router = useRouter()
2272
+
2273
+ // React.useEffect(() => {
2274
+ // if (!when) return
2275
+
2276
+ // let unblock = router.getHistory().block((transition) => {
2277
+ // if (window.confirm(message)) {
2278
+ // unblock()
2279
+ // transition.retry()
2280
+ // } else {
2281
+ // router.setStore((s) => {
2282
+ // s.currentLocation.pathname = window.location.pathname
2283
+ // })
3290
2284
  // }
3291
- // return value
3292
- // }) || ''
3293
- // )
2285
+ // })
2286
+
2287
+ // return unblock
2288
+ // }, [when, message])
2289
+ // }
2290
+
2291
+ // export function Prompt({ message, when, children }: PromptProps) {
2292
+ // usePrompt(message, when ?? true)
2293
+ // return (children ?? null) as ReactNode
3294
2294
  // }
3295
2295
 
3296
2296
  exports.DefaultErrorBoundary = DefaultErrorBoundary;
3297
2297
  exports.Link = Link;
3298
2298
  exports.MatchRoute = MatchRoute;
3299
2299
  exports.Outlet = Outlet;
3300
- exports.Prompt = Prompt;
2300
+ exports.ReactRouter = ReactRouter;
2301
+ exports.Route = Route;
2302
+ exports.RouteMatch = RouteMatch;
2303
+ exports.Router = Router;
3301
2304
  exports.RouterProvider = RouterProvider;
3302
- exports.__useStoreValue = __useStoreValue;
3303
2305
  exports.batch = batch;
3304
2306
  exports.cleanPath = cleanPath;
2307
+ exports.createAction = createAction;
3305
2308
  exports.createBrowserHistory = createBrowserHistory;
3306
- exports.createEffect = createEffect;
3307
2309
  exports.createHashHistory = createHashHistory;
3308
- exports.createMemo = createMemo;
3309
2310
  exports.createMemoryHistory = createMemoryHistory;
3310
- exports.createReactRouter = createReactRouter;
3311
- exports.createRoot = createRoot;
3312
- exports.createRoute = createRoute;
3313
2311
  exports.createRouteConfig = createRouteConfig;
3314
- exports.createRouteMatch = createRouteMatch;
3315
- exports.createRouter = createRouter;
3316
- exports.createSignal = createSignal;
3317
2312
  exports.createStore = createStore;
3318
2313
  exports.decode = decode;
2314
+ exports.defaultFetchServerDataFn = defaultFetchServerDataFn;
3319
2315
  exports.defaultParseSearch = defaultParseSearch;
3320
2316
  exports.defaultStringifySearch = defaultStringifySearch;
3321
2317
  exports.encode = encode;
3322
2318
  exports.functionalUpdate = functionalUpdate;
3323
2319
  exports.interpolatePath = interpolatePath;
3324
2320
  exports.invariant = invariant;
3325
- exports.isWrappable = isWrappable;
3326
2321
  exports.joinPaths = joinPaths;
3327
2322
  exports.last = last;
3328
2323
  exports.lazy = lazy;
3329
2324
  exports.matchByPath = matchByPath;
3330
2325
  exports.matchPathname = matchPathname;
3331
2326
  exports.matchesContext = matchesContext;
3332
- exports.onCleanup = onCleanup;
3333
2327
  exports.parsePathname = parsePathname;
3334
2328
  exports.parseSearchWith = parseSearchWith;
3335
2329
  exports.pick = pick;
2330
+ exports.replaceEqualDeep = replaceEqualDeep;
3336
2331
  exports.resolvePath = resolvePath;
3337
2332
  exports.rootRouteId = rootRouteId;
3338
2333
  exports.routerContext = routerContext;
3339
- exports.sharedClone = sharedClone;
3340
2334
  exports.stringifySearchWith = stringifySearchWith;
2335
+ exports.trackDeep = trackDeep;
3341
2336
  exports.trimPath = trimPath;
3342
2337
  exports.trimPathLeft = trimPathLeft;
3343
2338
  exports.trimPathRight = trimPathRight;
3344
- exports.untrack = untrack;
3345
- exports.unwrap = unwrap;
3346
2339
  exports.useAction = useAction;
3347
2340
  exports.useLinkProps = useLinkProps;
3348
2341
  exports.useLoaderData = useLoaderData;
@@ -3351,11 +2344,11 @@
3351
2344
  exports.useMatches = useMatches;
3352
2345
  exports.useNavigate = useNavigate;
3353
2346
  exports.useParams = useParams;
3354
- exports.usePrompt = usePrompt;
3355
2347
  exports.useRoute = useRoute;
3356
2348
  exports.useRouter = useRouter;
3357
2349
  exports.useRouterStore = useRouterStore;
3358
2350
  exports.useSearch = useSearch;
2351
+ exports.useStore = useStore;
3359
2352
  exports.warning = warning;
3360
2353
 
3361
2354
  Object.defineProperty(exports, '__esModule', { value: true });