@tanstack/router-core 1.146.0 → 1.146.2

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.
@@ -59,7 +59,9 @@ exports.stringifySearchWith = searchParams.stringifySearchWith;
59
59
  exports.createControlledPromise = utils.createControlledPromise;
60
60
  exports.decodePath = utils.decodePath;
61
61
  exports.deepEqual = utils.deepEqual;
62
+ exports.escapeHtml = utils.escapeHtml;
62
63
  exports.functionalUpdate = utils.functionalUpdate;
64
+ exports.isDangerousProtocol = utils.isDangerousProtocol;
63
65
  exports.isModuleNotFoundError = utils.isModuleNotFoundError;
64
66
  exports.isPlainArray = utils.isPlainArray;
65
67
  exports.isPlainObject = utils.isPlainObject;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -24,7 +24,7 @@ export { retainSearchParams, stripSearchParams } from './searchMiddleware.cjs';
24
24
  export { defaultParseSearch, defaultStringifySearch, parseSearchWith, stringifySearchWith, } from './searchParams.cjs';
25
25
  export type { SearchSerializer, SearchParser } from './searchParams.cjs';
26
26
  export type { OptionalStructuralSharing } from './structuralSharing.cjs';
27
- export { last, functionalUpdate, replaceEqualDeep, isPlainObject, isPlainArray, deepEqual, createControlledPromise, isModuleNotFoundError, decodePath, } from './utils.cjs';
27
+ export { last, functionalUpdate, replaceEqualDeep, isPlainObject, isPlainArray, deepEqual, createControlledPromise, isModuleNotFoundError, decodePath, escapeHtml, isDangerousProtocol, } from './utils.cjs';
28
28
  export type { NoInfer, IsAny, PickAsRequired, PickRequired, PickOptional, WithoutEmpty, Expand, DeepPartial, MakeDifferenceOptional, IsUnion, IsNonEmptyObject, Assign, IntersectAssign, Timeout, Updater, NonNullableUpdater, StringLiteral, ThrowOrOptional, ThrowConstraint, ControlledPromise, ExtractObjects, PartialMergeAllObject, MergeAllPrimitive, ExtractPrimitives, PartialMergeAll, Constrain, ConstrainLiteral, UnionToIntersection, MergeAllObjects, MergeAll, ValidateJSON, StrictOrFrom, LooseReturnType, LooseAsyncReturnType, Awaitable, } from './utils.cjs';
29
29
  export type { StandardSchemaValidatorProps, StandardSchemaValidator, AnyStandardSchemaValidator, StandardSchemaValidatorTypes, AnyStandardSchemaValidateSuccess, AnyStandardSchemaValidateFailure, AnyStandardSchemaValidateIssue, AnyStandardSchemaValidateInput, AnyStandardSchemaValidate, ValidatorObj, AnyValidatorObj, ValidatorAdapter, AnyValidatorAdapter, AnyValidatorFn, ValidatorFn, Validator, AnyValidator, AnySchema, DefaultValidator, ResolveSearchValidatorInputFn, ResolveSearchValidatorInput, ResolveValidatorInputFn, ResolveValidatorInput, ResolveValidatorOutputFn, ResolveValidatorOutput, } from './validators.cjs';
30
30
  export type { UseRouteContextBaseOptions, UseRouteContextOptions, UseRouteContextResult, } from './useRouteContext.cjs';
@@ -1,7 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const utils = require("./utils.cjs");
3
4
  function redirect(opts) {
4
5
  opts.statusCode = opts.statusCode || opts.code || 307;
6
+ if (typeof opts.href === "string" && utils.isDangerousProtocol(opts.href)) {
7
+ throw new Error(
8
+ `Redirect blocked: unsafe protocol in href "${opts.href}". Only ${utils.SAFE_URL_PROTOCOLS.join(", ")} protocols are allowed.`
9
+ );
10
+ }
5
11
  if (!opts.reloadDocument && typeof opts.href === "string") {
6
12
  try {
7
13
  new URL(opts.href);
@@ -1 +1 @@
1
- {"version":3,"file":"redirect.cjs","sources":["../../src/redirect.ts"],"sourcesContent":["import type { NavigateOptions } from './link'\nimport type { AnyRouter, RegisteredRouter } from './router'\n\nexport type AnyRedirect = Redirect<any, any, any, any, any>\n\n/**\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RedirectType)\n */\nexport type Redirect<\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends string = string,\n TTo extends string | undefined = undefined,\n TMaskFrom extends string = TFrom,\n TMaskTo extends string = '.',\n> = Response & {\n options: NavigateOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>\n redirectHandled?: boolean\n}\n\nexport type RedirectOptions<\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends string = string,\n TTo extends string | undefined = undefined,\n TMaskFrom extends string = TFrom,\n TMaskTo extends string = '.',\n> = {\n href?: string\n /**\n * @deprecated Use `statusCode` instead\n **/\n code?: number\n /**\n * The HTTP status code to use when redirecting.\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RedirectType#statuscode-property)\n */\n statusCode?: number\n /**\n * If provided, will throw the redirect object instead of returning it. This can be useful in places where `throwing` in a function might cause it to have a return type of `never`. In that case, you can use `redirect({ throw: true })` to throw the redirect object instead of returning it.\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RedirectType#throw-property)\n */\n throw?: any\n /**\n * The HTTP headers to use when redirecting.\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RedirectType#headers-property)\n */\n headers?: HeadersInit\n} & NavigateOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>\n\nexport type ResolvedRedirect<\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends string = string,\n TTo extends string = '',\n TMaskFrom extends string = TFrom,\n TMaskTo extends string = '',\n> = Redirect<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>\n\n/**\n * Create a redirect Response understood by TanStack Router.\n *\n * Use from route `loader`/`beforeLoad` or server functions to trigger a\n * navigation. If `throw: true` is set, the redirect is thrown instead of\n * returned. When an absolute `href` is supplied and `reloadDocument` is not\n * set, a full-document navigation is inferred.\n *\n * @param opts Options for the redirect. Common fields:\n * - `href`: absolute URL for external redirects; infers `reloadDocument`.\n * - `statusCode`: HTTP status code to use (defaults to 307).\n * - `headers`: additional headers to include on the Response.\n * - Standard navigation options like `to`, `params`, `search`, `replace`,\n * and `reloadDocument` for internal redirects.\n * @returns A Response augmented with router navigation options.\n * @link https://tanstack.com/router/latest/docs/framework/react/api/router/redirectFunction\n */\nexport function redirect<\n TRouter extends AnyRouter = RegisteredRouter,\n const TTo extends string | undefined = '.',\n const TFrom extends string = string,\n const TMaskFrom extends string = TFrom,\n const TMaskTo extends string = '',\n>(\n opts: RedirectOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>,\n): Redirect<TRouter, TFrom, TTo, TMaskFrom, TMaskTo> {\n opts.statusCode = opts.statusCode || opts.code || 307\n\n if (!opts.reloadDocument && typeof opts.href === 'string') {\n try {\n new URL(opts.href)\n opts.reloadDocument = true\n } catch {}\n }\n\n const headers = new Headers(opts.headers)\n if (opts.href && headers.get('Location') === null) {\n headers.set('Location', opts.href)\n }\n\n const response = new Response(null, {\n status: opts.statusCode,\n headers,\n })\n\n ;(response as Redirect<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>).options =\n opts\n\n if (opts.throw) {\n throw response\n }\n\n return response as Redirect<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>\n}\n\n/** Check whether a value is a TanStack Router redirect Response. */\n/** Check whether a value is a TanStack Router redirect Response. */\nexport function isRedirect(obj: any): obj is AnyRedirect {\n return obj instanceof Response && !!(obj as any).options\n}\n\n/** True if value is a redirect with a resolved `href` location. */\n/** True if value is a redirect with a resolved `href` location. */\nexport function isResolvedRedirect(\n obj: any,\n): obj is AnyRedirect & { options: { href: string } } {\n return isRedirect(obj) && !!obj.options.href\n}\n\n/** Parse a serialized redirect object back into a redirect Response. */\n/** Parse a serialized redirect object back into a redirect Response. */\nexport function parseRedirect(obj: any) {\n if (obj !== null && typeof obj === 'object' && obj.isSerializedRedirect) {\n return redirect(obj)\n }\n\n return undefined\n}\n"],"names":[],"mappings":";;AAyEO,SAAS,SAOd,MACmD;AACnD,OAAK,aAAa,KAAK,cAAc,KAAK,QAAQ;AAElD,MAAI,CAAC,KAAK,kBAAkB,OAAO,KAAK,SAAS,UAAU;AACzD,QAAI;AACF,UAAI,IAAI,KAAK,IAAI;AACjB,WAAK,iBAAiB;AAAA,IACxB,QAAQ;AAAA,IAAC;AAAA,EACX;AAEA,QAAM,UAAU,IAAI,QAAQ,KAAK,OAAO;AACxC,MAAI,KAAK,QAAQ,QAAQ,IAAI,UAAU,MAAM,MAAM;AACjD,YAAQ,IAAI,YAAY,KAAK,IAAI;AAAA,EACnC;AAEA,QAAM,WAAW,IAAI,SAAS,MAAM;AAAA,IAClC,QAAQ,KAAK;AAAA,IACb;AAAA,EAAA,CACD;AAEC,WAA+D,UAC/D;AAEF,MAAI,KAAK,OAAO;AACd,UAAM;AAAA,EACR;AAEA,SAAO;AACT;AAIO,SAAS,WAAW,KAA8B;AACvD,SAAO,eAAe,YAAY,CAAC,CAAE,IAAY;AACnD;AAIO,SAAS,mBACd,KACoD;AACpD,SAAO,WAAW,GAAG,KAAK,CAAC,CAAC,IAAI,QAAQ;AAC1C;AAIO,SAAS,cAAc,KAAU;AACtC,MAAI,QAAQ,QAAQ,OAAO,QAAQ,YAAY,IAAI,sBAAsB;AACvE,WAAO,SAAS,GAAG;AAAA,EACrB;AAEA,SAAO;AACT;;;;;"}
1
+ {"version":3,"file":"redirect.cjs","sources":["../../src/redirect.ts"],"sourcesContent":["import { SAFE_URL_PROTOCOLS, isDangerousProtocol } from './utils'\nimport type { NavigateOptions } from './link'\nimport type { AnyRouter, RegisteredRouter } from './router'\n\nexport type AnyRedirect = Redirect<any, any, any, any, any>\n\n/**\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RedirectType)\n */\nexport type Redirect<\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends string = string,\n TTo extends string | undefined = undefined,\n TMaskFrom extends string = TFrom,\n TMaskTo extends string = '.',\n> = Response & {\n options: NavigateOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>\n redirectHandled?: boolean\n}\n\nexport type RedirectOptions<\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends string = string,\n TTo extends string | undefined = undefined,\n TMaskFrom extends string = TFrom,\n TMaskTo extends string = '.',\n> = {\n href?: string\n /**\n * @deprecated Use `statusCode` instead\n **/\n code?: number\n /**\n * The HTTP status code to use when redirecting.\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RedirectType#statuscode-property)\n */\n statusCode?: number\n /**\n * If provided, will throw the redirect object instead of returning it. This can be useful in places where `throwing` in a function might cause it to have a return type of `never`. In that case, you can use `redirect({ throw: true })` to throw the redirect object instead of returning it.\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RedirectType#throw-property)\n */\n throw?: any\n /**\n * The HTTP headers to use when redirecting.\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RedirectType#headers-property)\n */\n headers?: HeadersInit\n} & NavigateOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>\n\nexport type ResolvedRedirect<\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends string = string,\n TTo extends string = '',\n TMaskFrom extends string = TFrom,\n TMaskTo extends string = '',\n> = Redirect<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>\n\n/**\n * Create a redirect Response understood by TanStack Router.\n *\n * Use from route `loader`/`beforeLoad` or server functions to trigger a\n * navigation. If `throw: true` is set, the redirect is thrown instead of\n * returned. When an absolute `href` is supplied and `reloadDocument` is not\n * set, a full-document navigation is inferred.\n *\n * @param opts Options for the redirect. Common fields:\n * - `href`: absolute URL for external redirects; infers `reloadDocument`.\n * - `statusCode`: HTTP status code to use (defaults to 307).\n * - `headers`: additional headers to include on the Response.\n * - Standard navigation options like `to`, `params`, `search`, `replace`,\n * and `reloadDocument` for internal redirects.\n * @returns A Response augmented with router navigation options.\n * @link https://tanstack.com/router/latest/docs/framework/react/api/router/redirectFunction\n */\nexport function redirect<\n TRouter extends AnyRouter = RegisteredRouter,\n const TTo extends string | undefined = '.',\n const TFrom extends string = string,\n const TMaskFrom extends string = TFrom,\n const TMaskTo extends string = '',\n>(\n opts: RedirectOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>,\n): Redirect<TRouter, TFrom, TTo, TMaskFrom, TMaskTo> {\n opts.statusCode = opts.statusCode || opts.code || 307\n\n // Block dangerous protocols in redirect href\n if (typeof opts.href === 'string' && isDangerousProtocol(opts.href)) {\n throw new Error(\n `Redirect blocked: unsafe protocol in href \"${opts.href}\". Only ${SAFE_URL_PROTOCOLS.join(', ')} protocols are allowed.`,\n )\n }\n\n if (!opts.reloadDocument && typeof opts.href === 'string') {\n try {\n new URL(opts.href)\n opts.reloadDocument = true\n } catch {}\n }\n\n const headers = new Headers(opts.headers)\n if (opts.href && headers.get('Location') === null) {\n headers.set('Location', opts.href)\n }\n\n const response = new Response(null, {\n status: opts.statusCode,\n headers,\n })\n\n ;(response as Redirect<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>).options =\n opts\n\n if (opts.throw) {\n throw response\n }\n\n return response as Redirect<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>\n}\n\n/** Check whether a value is a TanStack Router redirect Response. */\n/** Check whether a value is a TanStack Router redirect Response. */\nexport function isRedirect(obj: any): obj is AnyRedirect {\n return obj instanceof Response && !!(obj as any).options\n}\n\n/** True if value is a redirect with a resolved `href` location. */\n/** True if value is a redirect with a resolved `href` location. */\nexport function isResolvedRedirect(\n obj: any,\n): obj is AnyRedirect & { options: { href: string } } {\n return isRedirect(obj) && !!obj.options.href\n}\n\n/** Parse a serialized redirect object back into a redirect Response. */\n/** Parse a serialized redirect object back into a redirect Response. */\nexport function parseRedirect(obj: any) {\n if (obj !== null && typeof obj === 'object' && obj.isSerializedRedirect) {\n return redirect(obj)\n }\n\n return undefined\n}\n"],"names":["isDangerousProtocol","SAFE_URL_PROTOCOLS"],"mappings":";;;AA0EO,SAAS,SAOd,MACmD;AACnD,OAAK,aAAa,KAAK,cAAc,KAAK,QAAQ;AAGlD,MAAI,OAAO,KAAK,SAAS,YAAYA,MAAAA,oBAAoB,KAAK,IAAI,GAAG;AACnE,UAAM,IAAI;AAAA,MACR,8CAA8C,KAAK,IAAI,WAAWC,MAAAA,mBAAmB,KAAK,IAAI,CAAC;AAAA,IAAA;AAAA,EAEnG;AAEA,MAAI,CAAC,KAAK,kBAAkB,OAAO,KAAK,SAAS,UAAU;AACzD,QAAI;AACF,UAAI,IAAI,KAAK,IAAI;AACjB,WAAK,iBAAiB;AAAA,IACxB,QAAQ;AAAA,IAAC;AAAA,EACX;AAEA,QAAM,UAAU,IAAI,QAAQ,KAAK,OAAO;AACxC,MAAI,KAAK,QAAQ,QAAQ,IAAI,UAAU,MAAM,MAAM;AACjD,YAAQ,IAAI,YAAY,KAAK,IAAI;AAAA,EACnC;AAEA,QAAM,WAAW,IAAI,SAAS,MAAM;AAAA,IAClC,QAAQ,KAAK;AAAA,IACb;AAAA,EAAA,CACD;AAEC,WAA+D,UAC/D;AAEF,MAAI,KAAK,OAAO;AACd,UAAM;AAAA,EACR;AAEA,SAAO;AACT;AAIO,SAAS,WAAW,KAA8B;AACvD,SAAO,eAAe,YAAY,CAAC,CAAE,IAAY;AACnD;AAIO,SAAS,mBACd,KACoD;AACpD,SAAO,WAAW,GAAG,KAAK,CAAC,CAAC,IAAI,QAAQ;AAC1C;AAIO,SAAS,cAAc,KAAU;AACtC,MAAI,QAAQ,QAAQ,OAAO,QAAQ,YAAY,IAAI,sBAAsB;AACvE,WAAO,SAAS,GAAG;AAAA,EACrB;AAEA,SAAO;AACT;;;;;"}
@@ -517,7 +517,9 @@ class RouterCore {
517
517
  const parsed = history.parseHref(href, {
518
518
  __TSR_index: replace ? currentIndex : currentIndex + 1
519
519
  });
520
- rest.to = parsed.pathname;
520
+ const hrefUrl = new URL(parsed.pathname, this.origin);
521
+ const rewrittenUrl = rewrite.executeRewriteInput(this.rewrite, hrefUrl);
522
+ rest.to = rewrittenUrl.pathname;
521
523
  rest.search = this.options.parseSearch(parsed.search);
522
524
  rest.hash = parsed.hash.slice(1);
523
525
  }
@@ -560,12 +562,20 @@ class RouterCore {
560
562
  reloadDocument = true;
561
563
  }
562
564
  if (reloadDocument) {
563
- if (!href || !publicHref && !hrefIsUrl) {
565
+ if (to !== void 0 || !href) {
564
566
  const location = this.buildLocation({ to, ...rest });
565
567
  href = href ?? location.url.href;
566
568
  publicHref = publicHref ?? location.url.href;
567
569
  }
568
570
  const reloadHref = !hrefIsUrl && publicHref ? publicHref : href;
571
+ if (utils.isDangerousProtocol(reloadHref)) {
572
+ if (process.env.NODE_ENV !== "production") {
573
+ console.warn(
574
+ `Blocked navigation to dangerous protocol: ${reloadHref}`
575
+ );
576
+ }
577
+ return Promise.resolve();
578
+ }
569
579
  if (!rest.ignoreBlocker) {
570
580
  const historyWithBlockers = this.history;
571
581
  const blockers = historyWithBlockers.getBlockers?.() ?? [];
@@ -837,11 +847,22 @@ class RouterCore {
837
847
  return href;
838
848
  };
839
849
  this.resolveRedirect = (redirect2) => {
850
+ const locationHeader = redirect2.headers.get("Location");
840
851
  if (!redirect2.options.href) {
841
852
  const location = this.buildLocation(redirect2.options);
842
853
  const href = this.getParsedLocationHref(location);
843
- redirect2.options.href = location.href;
854
+ redirect2.options.href = href;
844
855
  redirect2.headers.set("Location", href);
856
+ } else if (locationHeader) {
857
+ try {
858
+ const url = new URL(locationHeader);
859
+ if (this.origin && url.origin === this.origin) {
860
+ const href = url.pathname + url.search + url.hash;
861
+ redirect2.options.href = href;
862
+ redirect2.headers.set("Location", href);
863
+ }
864
+ } catch {
865
+ }
845
866
  }
846
867
  if (!redirect2.headers.get("Location")) {
847
868
  redirect2.headers.set("Location", redirect2.options.href);