@tanstack/router-core 1.161.1 → 1.161.4

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.
@@ -1 +1 @@
1
- {"version":3,"file":"createRequestHandler.js","sources":["../../../src/ssr/createRequestHandler.ts"],"sourcesContent":["import { createMemoryHistory } from '@tanstack/history'\nimport { mergeHeaders } from './headers'\nimport {\n attachRouterServerSsrUtils,\n getNormalizedURL,\n getOrigin,\n} from './ssr-server'\nimport type { HandlerCallback } from './handlerCallback'\nimport type { AnyHeaders } from './headers'\nimport type { AnyRouter } from '../router'\nimport type { Manifest } from '../manifest'\n\nexport type RequestHandler<TRouter extends AnyRouter> = (\n cb: HandlerCallback<TRouter>,\n) => Promise<Response>\n\nexport function createRequestHandler<TRouter extends AnyRouter>({\n createRouter,\n request,\n getRouterManifest,\n}: {\n createRouter: () => TRouter\n request: Request\n getRouterManifest?: () => Manifest | Promise<Manifest>\n}): RequestHandler<TRouter> {\n return async (cb) => {\n const router = createRouter()\n // Track whether the callback will handle cleanup\n let cbWillCleanup = false\n\n try {\n attachRouterServerSsrUtils({\n router,\n manifest: await getRouterManifest?.(),\n })\n\n // normalizing and sanitizing the pathname here for server, so we always deal with the same format during SSR.\n const { url } = getNormalizedURL(request.url, 'http://localhost')\n const origin = getOrigin(request)\n const href = url.href.replace(url.origin, '')\n\n // Create a history for the router\n const history = createMemoryHistory({\n initialEntries: [href],\n })\n\n // Update the router with the history and context\n router.update({\n history,\n origin: router.options.origin ?? origin,\n })\n\n await router.load()\n\n await router.serverSsr?.dehydrate()\n\n const responseHeaders = getRequestHeaders({\n router,\n })\n\n // Mark that the callback will handle cleanup\n cbWillCleanup = true\n return cb({\n request,\n router,\n responseHeaders,\n } as any)\n } finally {\n if (!cbWillCleanup) {\n // Clean up router SSR state if the callback won't handle it\n // (e.g., if an error occurred before the callback was invoked).\n // When the callback runs, it handles cleanup (either via transformStreamWithRouter\n // for streaming, or directly in renderRouterToString for non-streaming).\n router.serverSsr?.cleanup()\n }\n }\n }\n}\n\nfunction getRequestHeaders(opts: { router: AnyRouter }): Headers {\n const matchHeaders = opts.router.state.matches.map<AnyHeaders>(\n (match) => match.headers,\n )\n\n // Handle Redirects\n const { redirect } = opts.router.state\n if (redirect) {\n matchHeaders.push(redirect.headers)\n }\n\n return mergeHeaders(\n {\n 'Content-Type': 'text/html; charset=UTF-8',\n },\n ...matchHeaders,\n )\n}\n"],"names":[],"mappings":";;;AAgBO,SAAS,qBAAgD;AAAA,EAC9D;AAAA,EACA;AAAA,EACA;AACF,GAI4B;AAC1B,SAAO,OAAO,OAAO;AACnB,UAAM,SAAS,aAAA;AAEf,QAAI,gBAAgB;AAEpB,QAAI;AACF,iCAA2B;AAAA,QACzB;AAAA,QACA,UAAU,MAAM,oBAAA;AAAA,MAAoB,CACrC;AAGD,YAAM,EAAE,IAAA,IAAQ,iBAAiB,QAAQ,KAAK,kBAAkB;AAChE,YAAM,SAAS,UAAU,OAAO;AAChC,YAAM,OAAO,IAAI,KAAK,QAAQ,IAAI,QAAQ,EAAE;AAG5C,YAAM,UAAU,oBAAoB;AAAA,QAClC,gBAAgB,CAAC,IAAI;AAAA,MAAA,CACtB;AAGD,aAAO,OAAO;AAAA,QACZ;AAAA,QACA,QAAQ,OAAO,QAAQ,UAAU;AAAA,MAAA,CAClC;AAED,YAAM,OAAO,KAAA;AAEb,YAAM,OAAO,WAAW,UAAA;AAExB,YAAM,kBAAkB,kBAAkB;AAAA,QACxC;AAAA,MAAA,CACD;AAGD,sBAAgB;AAChB,aAAO,GAAG;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MAAA,CACM;AAAA,IACV,UAAA;AACE,UAAI,CAAC,eAAe;AAKlB,eAAO,WAAW,QAAA;AAAA,MACpB;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,kBAAkB,MAAsC;AAC/D,QAAM,eAAe,KAAK,OAAO,MAAM,QAAQ;AAAA,IAC7C,CAAC,UAAU,MAAM;AAAA,EAAA;AAInB,QAAM,EAAE,SAAA,IAAa,KAAK,OAAO;AACjC,MAAI,UAAU;AACZ,iBAAa,KAAK,SAAS,OAAO;AAAA,EACpC;AAEA,SAAO;AAAA,IACL;AAAA,MACE,gBAAgB;AAAA,IAAA;AAAA,IAElB,GAAG;AAAA,EAAA;AAEP;"}
1
+ {"version":3,"file":"createRequestHandler.js","sources":["../../../src/ssr/createRequestHandler.ts"],"sourcesContent":["import { createMemoryHistory } from '@tanstack/history'\nimport { mergeHeaders } from './headers'\nimport {\n attachRouterServerSsrUtils,\n getNormalizedURL,\n getOrigin,\n} from './ssr-server'\nimport type { HandlerCallback } from './handlerCallback'\nimport type { AnyHeaders } from './headers'\nimport type { AnyRouter } from '../router'\nimport type { Manifest } from '../manifest'\n\nexport type RequestHandler<TRouter extends AnyRouter> = (\n cb: HandlerCallback<TRouter>,\n) => Promise<Response>\n\nexport function createRequestHandler<TRouter extends AnyRouter>({\n createRouter,\n request,\n getRouterManifest,\n}: {\n createRouter: () => TRouter\n request: Request\n getRouterManifest?: () => Manifest | Promise<Manifest>\n}): RequestHandler<TRouter> {\n return async (cb) => {\n const router = createRouter()\n // Track whether the callback will handle cleanup\n let cbWillCleanup = false\n\n try {\n attachRouterServerSsrUtils({\n router,\n manifest: await getRouterManifest?.(),\n })\n\n // normalizing and sanitizing the pathname here for server, so we always deal with the same format during SSR.\n const { url } = getNormalizedURL(request.url, 'http://localhost')\n const origin = getOrigin(request)\n const href = url.href.replace(url.origin, '')\n\n // Create a history for the router\n const history = createMemoryHistory({\n initialEntries: [href],\n })\n\n // Update the router with the history and context\n router.update({\n history,\n origin: router.options.origin ?? origin,\n })\n\n await router.load()\n\n await router.serverSsr?.dehydrate()\n\n const responseHeaders = getRequestHeaders({\n router,\n })\n\n // Mark that the callback will handle cleanup\n cbWillCleanup = true\n return cb({\n request,\n router,\n responseHeaders,\n })\n } finally {\n if (!cbWillCleanup) {\n // Clean up router SSR state if the callback won't handle it\n // (e.g., if an error occurred before the callback was invoked).\n // When the callback runs, it handles cleanup (either via transformStreamWithRouter\n // for streaming, or directly in renderRouterToString for non-streaming).\n router.serverSsr?.cleanup()\n }\n }\n }\n}\n\nfunction getRequestHeaders(opts: { router: AnyRouter }): Headers {\n const matchHeaders = opts.router.state.matches.map<AnyHeaders>(\n (match) => match.headers,\n )\n\n // Handle Redirects\n const { redirect } = opts.router.state\n if (redirect) {\n matchHeaders.push(redirect.headers)\n }\n\n return mergeHeaders(\n {\n 'Content-Type': 'text/html; charset=UTF-8',\n },\n ...matchHeaders,\n )\n}\n"],"names":[],"mappings":";;;AAgBO,SAAS,qBAAgD;AAAA,EAC9D;AAAA,EACA;AAAA,EACA;AACF,GAI4B;AAC1B,SAAO,OAAO,OAAO;AACnB,UAAM,SAAS,aAAA;AAEf,QAAI,gBAAgB;AAEpB,QAAI;AACF,iCAA2B;AAAA,QACzB;AAAA,QACA,UAAU,MAAM,oBAAA;AAAA,MAAoB,CACrC;AAGD,YAAM,EAAE,IAAA,IAAQ,iBAAiB,QAAQ,KAAK,kBAAkB;AAChE,YAAM,SAAS,UAAU,OAAO;AAChC,YAAM,OAAO,IAAI,KAAK,QAAQ,IAAI,QAAQ,EAAE;AAG5C,YAAM,UAAU,oBAAoB;AAAA,QAClC,gBAAgB,CAAC,IAAI;AAAA,MAAA,CACtB;AAGD,aAAO,OAAO;AAAA,QACZ;AAAA,QACA,QAAQ,OAAO,QAAQ,UAAU;AAAA,MAAA,CAClC;AAED,YAAM,OAAO,KAAA;AAEb,YAAM,OAAO,WAAW,UAAA;AAExB,YAAM,kBAAkB,kBAAkB;AAAA,QACxC;AAAA,MAAA,CACD;AAGD,sBAAgB;AAChB,aAAO,GAAG;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MAAA,CACD;AAAA,IACH,UAAA;AACE,UAAI,CAAC,eAAe;AAKlB,eAAO,WAAW,QAAA;AAAA,MACpB;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,kBAAkB,MAAsC;AAC/D,QAAM,eAAe,KAAK,OAAO,MAAM,QAAQ;AAAA,IAC7C,CAAC,UAAU,MAAM;AAAA,EAAA;AAInB,QAAM,EAAE,SAAA,IAAa,KAAK,OAAO;AACjC,MAAI,UAAU;AACZ,iBAAa,KAAK,SAAS,OAAO;AAAA,EACpC;AAEA,SAAO;AAAA,IACL;AAAA,MACE,gBAAgB;AAAA,IAAA;AAAA,IAElB,GAAG;AAAA,EAAA;AAEP;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/router-core",
3
- "version": "1.161.1",
3
+ "version": "1.161.4",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -141,19 +141,20 @@
141
141
  "src"
142
142
  ],
143
143
  "engines": {
144
- "node": ">=12"
144
+ "node": ">=20.19"
145
145
  },
146
146
  "dependencies": {
147
- "@tanstack/store": "^0.8.0",
147
+ "@tanstack/store": "^0.9.1",
148
148
  "cookie-es": "^2.0.0",
149
149
  "seroval": "^1.4.2",
150
150
  "seroval-plugins": "^1.4.2",
151
151
  "tiny-invariant": "^1.3.3",
152
152
  "tiny-warning": "^1.0.3",
153
- "@tanstack/history": "1.154.14"
153
+ "@tanstack/history": "1.161.4"
154
154
  },
155
155
  "devDependencies": {
156
- "esbuild": "^0.25.0"
156
+ "esbuild": "^0.25.0",
157
+ "vite": "*"
157
158
  },
158
159
  "scripts": {
159
160
  "clean": "rimraf ./dist && rimraf ./coverage",
package/src/location.ts CHANGED
@@ -25,7 +25,8 @@ export interface ParsedLocation<TSearchObj extends AnySchema = {}> {
25
25
  */
26
26
  state: ParsedHistoryState
27
27
  /**
28
- * The hash of the location, including the leading hash character.
28
+ * The hash of the location, excluding the leading hash character.
29
+ * (e.g., '123' instead of '#123')
29
30
  */
30
31
  hash: string
31
32
  /**
package/src/route.ts CHANGED
@@ -1942,7 +1942,7 @@ export class BaseRouteApi<TId, TRouter extends AnyRouter = RegisteredRouter> {
1942
1942
  id: TId
1943
1943
 
1944
1944
  constructor({ id }: { id: TId }) {
1945
- this.id = id as any
1945
+ this.id = id
1946
1946
  }
1947
1947
 
1948
1948
  notFound = (opts?: NotFoundError) => {
package/src/router.ts CHANGED
@@ -871,6 +871,13 @@ export function getLocationChangeInfo(routerState: {
871
871
  return { fromLocation, toLocation, pathChanged, hrefChanged, hashChanged }
872
872
  }
873
873
 
874
+ function filterRedirectedCachedMatches<T extends { status: string }>(
875
+ matches: Array<T>,
876
+ ): Array<T> {
877
+ const filtered = matches.filter((d) => d.status !== 'redirected')
878
+ return filtered.length === matches.length ? matches : filtered
879
+ }
880
+
874
881
  export type CreateRouterFn = <
875
882
  TRouteTree extends AnyRoute,
876
883
  TTrailingSlashOption extends TrailingSlashOption = 'never',
@@ -1123,16 +1130,7 @@ export class RouterCore<
1123
1130
  getInitialRouterState(this.latestLocation),
1124
1131
  ) as unknown as Store<any>
1125
1132
  } else {
1126
- this.__store = new Store(getInitialRouterState(this.latestLocation), {
1127
- onUpdate: () => {
1128
- this.__store.state = {
1129
- ...this.state,
1130
- cachedMatches: this.state.cachedMatches.filter(
1131
- (d) => !['redirected'].includes(d.status),
1132
- ),
1133
- }
1134
- },
1135
- })
1133
+ this.__store = new Store(getInitialRouterState(this.latestLocation))
1136
1134
 
1137
1135
  setupScrollRestoration(this)
1138
1136
  }
@@ -1175,10 +1173,10 @@ export class RouterCore<
1175
1173
  }
1176
1174
 
1177
1175
  if (needsLocationUpdate && this.__store) {
1178
- this.__store.state = {
1179
- ...this.state,
1176
+ this.__store.setState((s) => ({
1177
+ ...s,
1180
1178
  location: this.latestLocation,
1181
- }
1179
+ }))
1182
1180
  }
1183
1181
 
1184
1182
  if (
@@ -1885,8 +1883,16 @@ export class RouterCore<
1885
1883
  const fn =
1886
1884
  route.options.params?.stringify ?? route.options.stringifyParams
1887
1885
  if (fn) {
1888
- changedParams = true
1889
- Object.assign(nextParams, fn(nextParams))
1886
+ try {
1887
+ Object.assign(nextParams, fn(nextParams))
1888
+ changedParams = true
1889
+ } catch {
1890
+ // Ignore errors here. When a paired parseParams is defined,
1891
+ // extractStrictParams will re-throw during route matching,
1892
+ // storing the error on the match and allowing the route's
1893
+ // errorComponent to render. If no parseParams is defined,
1894
+ // the stringify error is silently dropped.
1895
+ }
1890
1896
  }
1891
1897
  }
1892
1898
  }
@@ -2445,7 +2451,9 @@ export class RouterCore<
2445
2451
  ...s.cachedMatches,
2446
2452
  ...exitingMatches.filter(
2447
2453
  (d) =>
2448
- d.status !== 'error' && d.status !== 'notFound',
2454
+ d.status !== 'error' &&
2455
+ d.status !== 'notFound' &&
2456
+ d.status !== 'redirected',
2449
2457
  ),
2450
2458
  ],
2451
2459
  }
@@ -2600,12 +2608,21 @@ export class RouterCore<
2600
2608
  : ''
2601
2609
 
2602
2610
  if (matchesKey) {
2603
- this.__store.setState((s) => ({
2604
- ...s,
2605
- [matchesKey]: s[matchesKey]?.map((d) =>
2606
- d.id === id ? updater(d) : d,
2607
- ),
2608
- }))
2611
+ if (matchesKey === 'cachedMatches') {
2612
+ this.__store.setState((s) => ({
2613
+ ...s,
2614
+ cachedMatches: filterRedirectedCachedMatches(
2615
+ s.cachedMatches.map((d) => (d.id === id ? updater(d) : d)),
2616
+ ),
2617
+ }))
2618
+ } else {
2619
+ this.__store.setState((s) => ({
2620
+ ...s,
2621
+ [matchesKey]: s[matchesKey]?.map((d) =>
2622
+ d.id === id ? updater(d) : d,
2623
+ ),
2624
+ }))
2625
+ }
2609
2626
  }
2610
2627
  })
2611
2628
  }
@@ -2839,10 +2856,7 @@ export class RouterCore<
2839
2856
  const matchLocation = {
2840
2857
  ...location,
2841
2858
  to: location.to
2842
- ? this.resolvePathWithBase(
2843
- (location.from || '') as string,
2844
- location.to as string,
2845
- )
2859
+ ? this.resolvePathWithBase(location.from || '', location.to as string)
2846
2860
  : undefined,
2847
2861
  params: location.params || {},
2848
2862
  leaveParams: true,
@@ -64,7 +64,7 @@ export function createRequestHandler<TRouter extends AnyRouter>({
64
64
  request,
65
65
  router,
66
66
  responseHeaders,
67
- } as any)
67
+ })
68
68
  } finally {
69
69
  if (!cbWillCleanup) {
70
70
  // Clean up router SSR state if the callback won't handle it