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

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,13 @@
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
  });
1285
+ } else {
1286
+ // If we're preloading, clean preload matches before we try and use them
1287
+ this.cleanMatches();
1257
1288
  }
1258
- this.cleanMatches();
1259
1289
  let firstBadMatchIndex;
1260
1290
 
1261
1291
  // Check each match middleware to see if the route can be accessed
@@ -1317,8 +1347,7 @@
1317
1347
  matchPromises.push((async () => {
1318
1348
  const parentMatchPromise = matchPromises[index - 1];
1319
1349
  const route = this.getRoute(match.routeId);
1320
- if (match.isFetching || match.status === 'success' && !this.getIsInvalid({
1321
- matchId: match.id,
1350
+ if (match.isFetching || match.status === 'success' && !isMatchInvalid(match, {
1322
1351
  preload: opts?.preload
1323
1352
  })) {
1324
1353
  return this.getRouteMatch(match.id)?.loadPromise;
@@ -1391,7 +1420,6 @@
1391
1420
  })());
1392
1421
  });
1393
1422
  await Promise.all(matchPromises);
1394
- this.cleanMatches();
1395
1423
  };
1396
1424
  reload = () => {
1397
1425
  return this.navigate({
@@ -1576,7 +1604,7 @@
1576
1604
  dehydrate = () => {
1577
1605
  return {
1578
1606
  state: {
1579
- dehydratedMatches: this.state.matches.map(d => pick(d, ['fetchedAt', 'invalid', 'invalidAt', 'id', 'loaderData', 'status', 'updatedAt']))
1607
+ dehydratedMatches: this.state.matches.map(d => pick(d, ['fetchedAt', 'invalid', 'preloadMaxAge', 'maxAge', 'id', 'loaderData', 'status', 'updatedAt']))
1580
1608
  }
1581
1609
  };
1582
1610
  };
@@ -1815,7 +1843,6 @@
1815
1843
  ...next.state
1816
1844
  });
1817
1845
  this.resetNextScroll = location.resetScroll ?? true;
1818
- console.log('resetScroll', this.resetNextScroll);
1819
1846
  return this.latestLoadPromise;
1820
1847
  };
1821
1848
  getRouteMatch = id => {
@@ -1840,17 +1867,17 @@
1840
1867
  if (!match) return;
1841
1868
  const route = this.getRoute(match.routeId);
1842
1869
  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);
1870
+ const preloadMaxAge = opts?.maxAge ?? route.options.preloadMaxAge ?? this.options.defaultPreloadMaxAge ?? 5000;
1871
+ const maxAge = opts?.maxAge ?? route.options.maxAge ?? this.options.defaultMaxAge ?? -1;
1845
1872
  this.setRouteMatch(id, s => ({
1846
1873
  ...s,
1847
1874
  error: undefined,
1848
1875
  status: 'success',
1849
1876
  isFetching: false,
1850
- updatedAt: Date.now(),
1877
+ updatedAt: updatedAt,
1851
1878
  loaderData: functionalUpdate(updater, s.loaderData),
1852
- preloadInvalidAt,
1853
- invalidAt
1879
+ preloadMaxAge,
1880
+ maxAge
1854
1881
  }));
1855
1882
  };
1856
1883
  invalidate = async opts => {
@@ -1881,20 +1908,6 @@
1881
1908
  return this.reload();
1882
1909
  }
1883
1910
  };
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
1911
  }
1899
1912
 
1900
1913
  // Detect if we're in the DOM
@@ -1940,6 +1953,16 @@
1940
1953
  return imported[key || 'default'](...args);
1941
1954
  };
1942
1955
  }
1956
+ function isMatchInvalid(match, opts) {
1957
+ const now = Date.now();
1958
+ if (match.invalid) {
1959
+ return true;
1960
+ }
1961
+ if (opts?.preload) {
1962
+ return match.preloadMaxAge < 0 ? false : match.updatedAt + match.preloadMaxAge < now;
1963
+ }
1964
+ return match.maxAge < 0 ? false : match.updatedAt + match.maxAge < now;
1965
+ }
1943
1966
 
1944
1967
  const windowKey = 'window';
1945
1968
  const delimiter = '___';
@@ -2106,6 +2129,7 @@
2106
2129
  exports.interpolatePath = interpolatePath;
2107
2130
  exports.invariant = invariant;
2108
2131
  exports.isDehydratedDeferred = isDehydratedDeferred;
2132
+ exports.isMatchInvalid = isMatchInvalid;
2109
2133
  exports.isPlainObject = isPlainObject;
2110
2134
  exports.isRedirect = isRedirect;
2111
2135
  exports.joinPaths = joinPaths;