@tanstack/router-core 0.0.1-beta.174 → 0.0.1-beta.176

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.
@@ -0,0 +1,53 @@
1
+ export type NoInfer<T> = [T][T extends any ? 0 : never];
2
+ export type IsAny<T, Y, N> = 1 extends 0 & T ? Y : N;
3
+ export type IsAnyBoolean<T> = 1 extends 0 & T ? true : false;
4
+ export type IsKnown<T, Y, N> = unknown extends T ? N : Y;
5
+ export type PickAsRequired<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>;
6
+ export type PickAsPartial<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
7
+ export type PickUnsafe<T, K> = K extends keyof T ? Pick<T, K> : never;
8
+ export type PickExtra<T, K> = {
9
+ [TKey in keyof K as string extends TKey ? never : TKey extends keyof T ? never : TKey]: K[TKey];
10
+ };
11
+ export type PickRequired<T> = {
12
+ [K in keyof T as undefined extends T[K] ? never : K]: T[K];
13
+ };
14
+ export type Expand<T> = T extends object ? T extends infer O ? {
15
+ [K in keyof O]: O[K];
16
+ } : never : T;
17
+ export type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => any ? I : never;
18
+ type Compute<T> = {
19
+ [K in keyof T]: T[K];
20
+ } | never;
21
+ type AllKeys<T> = T extends any ? keyof T : never;
22
+ export type MergeUnion<T, Keys extends keyof T = keyof T> = Compute<{
23
+ [K in Keys]: T[Keys];
24
+ } & {
25
+ [K in AllKeys<T>]?: T extends any ? K extends keyof T ? T[K] : never : never;
26
+ }>;
27
+ export type Values<O> = O[ValueKeys<O>];
28
+ export type ValueKeys<O> = Extract<keyof O, PropertyKey>;
29
+ export type DeepAwaited<T> = T extends Promise<infer A> ? DeepAwaited<A> : T extends Record<infer A, Promise<infer B>> ? {
30
+ [K in A]: DeepAwaited<B>;
31
+ } : T;
32
+ export type PathParamMask<TRoutePath extends string> = TRoutePath extends `${infer L}/$${infer C}/${infer R}` ? PathParamMask<`${L}/${string}/${R}`> : TRoutePath extends `${infer L}/$${infer C}` ? PathParamMask<`${L}/${string}`> : TRoutePath;
33
+ export type Timeout = ReturnType<typeof setTimeout>;
34
+ export type Updater<TPrevious, TResult = TPrevious> = TResult | ((prev?: TPrevious) => TResult);
35
+ export type PickExtract<T, U> = {
36
+ [K in keyof T as T[K] extends U ? K : never]: T[K];
37
+ };
38
+ export type PickExclude<T, U> = {
39
+ [K in keyof T as T[K] extends U ? never : K]: T[K];
40
+ };
41
+ export declare function last<T>(arr: T[]): T | undefined;
42
+ export declare function functionalUpdate<TResult>(updater: Updater<TResult>, previous: TResult): TResult;
43
+ export declare function pick<T, K extends keyof T>(parent: T, keys: K[]): Pick<T, K>;
44
+ /**
45
+ * This function returns `a` if `b` is deeply equal.
46
+ * If not, it will replace any deeply equal children of `b` with those of `a`.
47
+ * This can be used for structural sharing between immutable JSON values for example.
48
+ * Do not use this with signals
49
+ */
50
+ export declare function replaceEqualDeep<T>(prev: any, _next: T): T;
51
+ export declare function isPlainObject(o: any): boolean;
52
+ export declare function partialDeepEqual(a: any, b: any): boolean;
53
+ export {};
@@ -9,8 +9,8 @@
9
9
  * @license MIT
10
10
  */
11
11
  (function (global, factory) {
12
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('use-sync-external-store/shim/with-selector')) :
13
- typeof define === 'function' && define.amd ? define(['exports', 'use-sync-external-store/shim/with-selector'], factory) :
12
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
13
+ typeof define === 'function' && define.amd ? define(['exports'], factory) :
14
14
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.RouterCore = {}));
15
15
  })(this, (function (exports) { 'use strict';
16
16
 
@@ -804,6 +804,8 @@
804
804
  //
805
805
 
806
806
  const componentTypes = ['component', 'errorComponent', 'pendingComponent'];
807
+ const visibilityChangeEvent = 'visibilitychange';
808
+ const focusEvent = 'focus';
807
809
  class Router {
808
810
  #unsubHistory;
809
811
  resetNextScroll = false;
@@ -880,12 +882,26 @@
880
882
  this.__store.setState(s => Object.assign(s, getInitialRouterState()));
881
883
  };
882
884
  mount = () => {
883
- // If the router matches are empty, start loading the matches
884
- // if (!this.state.matches.length) {
885
+ // addEventListener does not exist in React Native, but window does
886
+ // In the future, we might need to invert control here for more adapters
887
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
888
+ if (typeof window !== 'undefined' && window.addEventListener) {
889
+ window.addEventListener(visibilityChangeEvent, this.#onFocus, false);
890
+ window.addEventListener(focusEvent, this.#onFocus, false);
891
+ }
885
892
  this.safeLoad();
886
- // }
893
+ return () => {
894
+ if (typeof window !== 'undefined' && window.removeEventListener) {
895
+ window.removeEventListener(visibilityChangeEvent, this.#onFocus);
896
+ window.removeEventListener(focusEvent, this.#onFocus);
897
+ }
898
+ };
899
+ };
900
+ #onFocus = () => {
901
+ if (this.options.refetchOnWindowFocus ?? true) {
902
+ this.reload();
903
+ }
887
904
  };
888
-
889
905
  update = opts => {
890
906
  this.options = {
891
907
  ...this.options,
@@ -956,7 +972,6 @@
956
972
  };
957
973
 
958
974
  // Cancel any pending matches
959
- // this.cancelMatches()
960
975
 
961
976
  let pendingMatches;
962
977
  this.#emit({
@@ -999,6 +1014,9 @@
999
1014
  if (latestPromise = checkLatest()) {
1000
1015
  return latestPromise;
1001
1016
  }
1017
+ const exitingMatchIds = this.state.matchIds.filter(id => !this.state.pendingMatchIds.includes(id));
1018
+ const enteringMatchIds = this.state.pendingMatchIds.filter(id => !this.state.matchIds.includes(id));
1019
+ const stayingMatchIds = this.state.matchIds.filter(id => this.state.pendingMatchIds.includes(id));
1002
1020
  this.__store.setState(s => ({
1003
1021
  ...s,
1004
1022
  status: 'idle',
@@ -1006,6 +1024,13 @@
1006
1024
  matchIds: s.pendingMatchIds,
1007
1025
  pendingMatchIds: []
1008
1026
  }));
1027
+ [[exitingMatchIds, 'onLeave'], [enteringMatchIds, 'onEnter'], [stayingMatchIds, 'onTransition']].forEach(([matchIds, hook]) => {
1028
+ matchIds.forEach(id => {
1029
+ const match = this.getRouteMatch(id);
1030
+ const route = this.getRoute(match.routeId);
1031
+ route.options[hook]?.(match);
1032
+ });
1033
+ });
1009
1034
  this.#emit({
1010
1035
  type: 'onLoad',
1011
1036
  from: prevLocation,
@@ -1022,6 +1047,9 @@
1022
1047
  }
1023
1048
  });
1024
1049
  this.latestLoadPromise = promise;
1050
+ this.latestLoadPromise.then(() => {
1051
+ this.cleanMatches();
1052
+ });
1025
1053
  return this.latestLoadPromise;
1026
1054
  };
1027
1055
  #mergeMatches = (prevMatchesById, nextMatches) => {
@@ -1066,7 +1094,7 @@
1066
1094
  const now = Date.now();
1067
1095
  const outdatedMatchIds = Object.values(this.state.matchesById).filter(match => {
1068
1096
  const route = this.getRoute(match.routeId);
1069
- return !this.state.matchIds.includes(match.id) && !this.state.pendingMatchIds.includes(match.id) && match.preloadInvalidAt < now && (route.options.gcMaxAge ? match.updatedAt + route.options.gcMaxAge < now : true);
1097
+ return !this.state.matchIds.includes(match.id) && !this.state.pendingMatchIds.includes(match.id) && (match.preloadMaxAge > -1 ? match.updatedAt + match.preloadMaxAge < now : true) && (route.options.gcMaxAge ? match.updatedAt + route.options.gcMaxAge < now : true);
1070
1098
  }).map(d => d.id);
1071
1099
  if (outdatedMatchIds.length) {
1072
1100
  this.__store.setState(s => {
@@ -1154,8 +1182,8 @@
1154
1182
  params: routeParams,
1155
1183
  pathname: joinPaths([this.basepath, interpolatedPath]),
1156
1184
  updatedAt: Date.now(),
1157
- invalidAt: Infinity,
1158
- preloadInvalidAt: Infinity,
1185
+ maxAge: -1,
1186
+ preloadMaxAge: -1,
1159
1187
  routeSearch: {},
1160
1188
  search: {},
1161
1189
  status: hasLoaders ? 'pending' : 'success',
@@ -1251,11 +1279,10 @@
1251
1279
  paramsError: match.paramsError,
1252
1280
  searchError: match.searchError,
1253
1281
  params: match.params,
1254
- preloadInvalidAt: 0
1282
+ preloadMaxAge: 0
1255
1283
  }));
1256
1284
  });
1257
1285
  }
1258
- this.cleanMatches();
1259
1286
  let firstBadMatchIndex;
1260
1287
 
1261
1288
  // Check each match middleware to see if the route can be accessed
@@ -1317,8 +1344,7 @@
1317
1344
  matchPromises.push((async () => {
1318
1345
  const parentMatchPromise = matchPromises[index - 1];
1319
1346
  const route = this.getRoute(match.routeId);
1320
- if (match.isFetching || match.status === 'success' && !this.getIsInvalid({
1321
- matchId: match.id,
1347
+ if (match.isFetching || match.status === 'success' && !isMatchInvalid(match, {
1322
1348
  preload: opts?.preload
1323
1349
  })) {
1324
1350
  return this.getRouteMatch(match.id)?.loadPromise;
@@ -1391,7 +1417,6 @@
1391
1417
  })());
1392
1418
  });
1393
1419
  await Promise.all(matchPromises);
1394
- this.cleanMatches();
1395
1420
  };
1396
1421
  reload = () => {
1397
1422
  return this.navigate({
@@ -1576,7 +1601,7 @@
1576
1601
  dehydrate = () => {
1577
1602
  return {
1578
1603
  state: {
1579
- dehydratedMatches: this.state.matches.map(d => pick(d, ['fetchedAt', 'invalid', 'invalidAt', 'id', 'loaderData', 'status', 'updatedAt']))
1604
+ dehydratedMatches: this.state.matches.map(d => pick(d, ['fetchedAt', 'invalid', 'preloadMaxAge', 'maxAge', 'id', 'loaderData', 'status', 'updatedAt']))
1580
1605
  }
1581
1606
  };
1582
1607
  };
@@ -1815,7 +1840,6 @@
1815
1840
  ...next.state
1816
1841
  });
1817
1842
  this.resetNextScroll = location.resetScroll ?? true;
1818
- console.log('resetScroll', this.resetNextScroll);
1819
1843
  return this.latestLoadPromise;
1820
1844
  };
1821
1845
  getRouteMatch = id => {
@@ -1840,17 +1864,17 @@
1840
1864
  if (!match) return;
1841
1865
  const route = this.getRoute(match.routeId);
1842
1866
  const updatedAt = opts?.updatedAt ?? Date.now();
1843
- const preloadInvalidAt = updatedAt + (opts?.maxAge ?? route.options.preloadMaxAge ?? this.options.defaultPreloadMaxAge ?? 5000);
1844
- const invalidAt = updatedAt + (opts?.maxAge ?? route.options.maxAge ?? this.options.defaultMaxAge ?? Infinity);
1867
+ const preloadMaxAge = opts?.maxAge ?? route.options.preloadMaxAge ?? this.options.defaultPreloadMaxAge ?? 5000;
1868
+ const maxAge = opts?.maxAge ?? route.options.maxAge ?? this.options.defaultMaxAge ?? -1;
1845
1869
  this.setRouteMatch(id, s => ({
1846
1870
  ...s,
1847
1871
  error: undefined,
1848
1872
  status: 'success',
1849
1873
  isFetching: false,
1850
- updatedAt: Date.now(),
1874
+ updatedAt: updatedAt,
1851
1875
  loaderData: functionalUpdate(updater, s.loaderData),
1852
- preloadInvalidAt,
1853
- invalidAt
1876
+ preloadMaxAge,
1877
+ maxAge
1854
1878
  }));
1855
1879
  };
1856
1880
  invalidate = async opts => {
@@ -1881,20 +1905,6 @@
1881
1905
  return this.reload();
1882
1906
  }
1883
1907
  };
1884
- getIsInvalid = opts => {
1885
- if (!opts?.matchId) {
1886
- return !!this.state.matches.find(d => this.getIsInvalid({
1887
- matchId: d.id,
1888
- preload: opts?.preload
1889
- }));
1890
- }
1891
- const match = this.getRouteMatch(opts?.matchId);
1892
- if (!match) {
1893
- return false;
1894
- }
1895
- const now = Date.now();
1896
- return match.invalid || (opts?.preload ? match.preloadInvalidAt : match.invalidAt) < now;
1897
- };
1898
1908
  }
1899
1909
 
1900
1910
  // Detect if we're in the DOM
@@ -1940,6 +1950,16 @@
1940
1950
  return imported[key || 'default'](...args);
1941
1951
  };
1942
1952
  }
1953
+ function isMatchInvalid(match, opts) {
1954
+ const now = Date.now();
1955
+ if (match.invalid) {
1956
+ return true;
1957
+ }
1958
+ if (opts?.preload) {
1959
+ return match.preloadMaxAge < 0 ? false : match.updatedAt + match.preloadMaxAge < now;
1960
+ }
1961
+ return match.maxAge < 0 ? false : match.updatedAt + match.maxAge < now;
1962
+ }
1943
1963
 
1944
1964
  const windowKey = 'window';
1945
1965
  const delimiter = '___';
@@ -2106,6 +2126,7 @@
2106
2126
  exports.interpolatePath = interpolatePath;
2107
2127
  exports.invariant = invariant;
2108
2128
  exports.isDehydratedDeferred = isDehydratedDeferred;
2129
+ exports.isMatchInvalid = isMatchInvalid;
2109
2130
  exports.isPlainObject = isPlainObject;
2110
2131
  exports.isRedirect = isRedirect;
2111
2132
  exports.joinPaths = joinPaths;