@tanstack/router-core 1.157.10 → 1.157.12

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.
@@ -43,19 +43,19 @@ function createRequestHandler({
43
43
  };
44
44
  }
45
45
  function getRequestHeaders(opts) {
46
- let headers = mergeHeaders(
47
- {
48
- "Content-Type": "text/html; charset=UTF-8"
49
- },
50
- ...opts.router.state.matches.map((match) => {
51
- return match.headers;
52
- })
46
+ const matchHeaders = opts.router.state.matches.map(
47
+ (match) => match.headers
53
48
  );
54
49
  const { redirect } = opts.router.state;
55
50
  if (redirect) {
56
- headers = mergeHeaders(headers, redirect.headers);
51
+ matchHeaders.push(redirect.headers);
57
52
  }
58
- return headers;
53
+ return mergeHeaders(
54
+ {
55
+ "Content-Type": "text/html; charset=UTF-8"
56
+ },
57
+ ...matchHeaders
58
+ );
59
59
  }
60
60
  export {
61
61
  createRequestHandler
@@ -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 { 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 let headers = mergeHeaders(\n {\n 'Content-Type': 'text/html; charset=UTF-8',\n },\n ...opts.router.state.matches.map((match) => {\n return match.headers\n }),\n )\n\n // Handle Redirects\n const { redirect } = opts.router.state\n\n if (redirect) {\n headers = mergeHeaders(headers, redirect.headers)\n }\n\n return headers\n}\n"],"names":[],"mappings":";;;AAeO,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,MAAM,iBAAiB,QAAQ,KAAK,kBAAkB;AAC5D,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,MAAI,UAAU;AAAA,IACZ;AAAA,MACE,gBAAgB;AAAA,IAAA;AAAA,IAElB,GAAG,KAAK,OAAO,MAAM,QAAQ,IAAI,CAAC,UAAU;AAC1C,aAAO,MAAM;AAAA,IACf,CAAC;AAAA,EAAA;AAIH,QAAM,EAAE,SAAA,IAAa,KAAK,OAAO;AAEjC,MAAI,UAAU;AACZ,cAAU,aAAa,SAAS,SAAS,OAAO;AAAA,EAClD;AAEA,SAAO;AACT;"}
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,MAAM,iBAAiB,QAAQ,KAAK,kBAAkB;AAC5D,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,5 +1,4 @@
1
1
  import { OutgoingHttpHeaders } from 'node:http2';
2
2
  export declare function headersInitToObject(headers: HeadersInit): Record<keyof OutgoingHttpHeaders, string>;
3
- type AnyHeaders = Headers | HeadersInit | Record<string, string> | Array<[string, string]> | OutgoingHttpHeaders | undefined;
3
+ export type AnyHeaders = Headers | HeadersInit | Record<string, string> | Array<[string, string]> | OutgoingHttpHeaders | undefined;
4
4
  export declare function mergeHeaders(...headers: Array<AnyHeaders>): Headers;
5
- export {};
@@ -9,18 +9,19 @@ function headersInitToObject(headers) {
9
9
  }
10
10
  function toHeadersInstance(init) {
11
11
  if (init instanceof Headers) {
12
- return new Headers(init);
12
+ return init;
13
13
  } else if (Array.isArray(init)) {
14
14
  return new Headers(init);
15
15
  } else if (typeof init === "object") {
16
16
  return new Headers(init);
17
17
  } else {
18
- return new Headers();
18
+ return null;
19
19
  }
20
20
  }
21
21
  function mergeHeaders(...headers) {
22
22
  return headers.reduce((acc, header) => {
23
23
  const headersInstance = toHeadersInstance(header);
24
+ if (!headersInstance) return acc;
24
25
  for (const [key, value] of headersInstance.entries()) {
25
26
  if (key === "set-cookie") {
26
27
  const splitCookies = splitSetCookieString(value);
@@ -1 +1 @@
1
- {"version":3,"file":"headers.js","sources":["../../../src/ssr/headers.ts"],"sourcesContent":["import { splitSetCookieString } from 'cookie-es'\nimport type { OutgoingHttpHeaders } from 'node:http2'\n\n// A utility function to turn HeadersInit into an object\nexport function headersInitToObject(\n headers: HeadersInit,\n): Record<keyof OutgoingHttpHeaders, string> {\n const obj: Record<keyof OutgoingHttpHeaders, string> = {}\n const headersInstance = new Headers(headers)\n for (const [key, value] of headersInstance.entries()) {\n obj[key] = value\n }\n return obj\n}\n\ntype AnyHeaders =\n | Headers\n | HeadersInit\n | Record<string, string>\n | Array<[string, string]>\n | OutgoingHttpHeaders\n | undefined\n\n// Helper function to convert various HeaderInit types to a Headers instance\nfunction toHeadersInstance(init: AnyHeaders) {\n if (init instanceof Headers) {\n return new Headers(init)\n } else if (Array.isArray(init)) {\n return new Headers(init)\n } else if (typeof init === 'object') {\n return new Headers(init as HeadersInit)\n } else {\n return new Headers()\n }\n}\n\n// Function to merge headers with proper overrides\nexport function mergeHeaders(...headers: Array<AnyHeaders>) {\n return headers.reduce((acc: Headers, header) => {\n const headersInstance = toHeadersInstance(header)\n for (const [key, value] of headersInstance.entries()) {\n if (key === 'set-cookie') {\n const splitCookies = splitSetCookieString(value)\n splitCookies.forEach((cookie) => acc.append('set-cookie', cookie))\n } else {\n acc.set(key, value)\n }\n }\n return acc\n }, new Headers())\n}\n"],"names":[],"mappings":";AAIO,SAAS,oBACd,SAC2C;AAC3C,QAAM,MAAiD,CAAA;AACvD,QAAM,kBAAkB,IAAI,QAAQ,OAAO;AAC3C,aAAW,CAAC,KAAK,KAAK,KAAK,gBAAgB,WAAW;AACpD,QAAI,GAAG,IAAI;AAAA,EACb;AACA,SAAO;AACT;AAWA,SAAS,kBAAkB,MAAkB;AAC3C,MAAI,gBAAgB,SAAS;AAC3B,WAAO,IAAI,QAAQ,IAAI;AAAA,EACzB,WAAW,MAAM,QAAQ,IAAI,GAAG;AAC9B,WAAO,IAAI,QAAQ,IAAI;AAAA,EACzB,WAAW,OAAO,SAAS,UAAU;AACnC,WAAO,IAAI,QAAQ,IAAmB;AAAA,EACxC,OAAO;AACL,WAAO,IAAI,QAAA;AAAA,EACb;AACF;AAGO,SAAS,gBAAgB,SAA4B;AAC1D,SAAO,QAAQ,OAAO,CAAC,KAAc,WAAW;AAC9C,UAAM,kBAAkB,kBAAkB,MAAM;AAChD,eAAW,CAAC,KAAK,KAAK,KAAK,gBAAgB,WAAW;AACpD,UAAI,QAAQ,cAAc;AACxB,cAAM,eAAe,qBAAqB,KAAK;AAC/C,qBAAa,QAAQ,CAAC,WAAW,IAAI,OAAO,cAAc,MAAM,CAAC;AAAA,MACnE,OAAO;AACL,YAAI,IAAI,KAAK,KAAK;AAAA,MACpB;AAAA,IACF;AACA,WAAO;AAAA,EACT,GAAG,IAAI,SAAS;AAClB;"}
1
+ {"version":3,"file":"headers.js","sources":["../../../src/ssr/headers.ts"],"sourcesContent":["import { splitSetCookieString } from 'cookie-es'\nimport type { OutgoingHttpHeaders } from 'node:http2'\n\n// A utility function to turn HeadersInit into an object\nexport function headersInitToObject(\n headers: HeadersInit,\n): Record<keyof OutgoingHttpHeaders, string> {\n const obj: Record<keyof OutgoingHttpHeaders, string> = {}\n const headersInstance = new Headers(headers)\n for (const [key, value] of headersInstance.entries()) {\n obj[key] = value\n }\n return obj\n}\n\nexport type AnyHeaders =\n | Headers\n | HeadersInit\n | Record<string, string>\n | Array<[string, string]>\n | OutgoingHttpHeaders\n | undefined\n\n// Helper function to convert various HeaderInit types to a Headers instance\nfunction toHeadersInstance(init: AnyHeaders) {\n if (init instanceof Headers) {\n return init\n } else if (Array.isArray(init)) {\n return new Headers(init)\n } else if (typeof init === 'object') {\n return new Headers(init as HeadersInit)\n } else {\n return null\n }\n}\n\n// Function to merge headers with proper overrides\nexport function mergeHeaders(...headers: Array<AnyHeaders>) {\n return headers.reduce((acc: Headers, header) => {\n const headersInstance = toHeadersInstance(header)\n if (!headersInstance) return acc\n for (const [key, value] of headersInstance.entries()) {\n if (key === 'set-cookie') {\n const splitCookies = splitSetCookieString(value)\n splitCookies.forEach((cookie) => acc.append('set-cookie', cookie))\n } else {\n acc.set(key, value)\n }\n }\n return acc\n }, new Headers())\n}\n"],"names":[],"mappings":";AAIO,SAAS,oBACd,SAC2C;AAC3C,QAAM,MAAiD,CAAA;AACvD,QAAM,kBAAkB,IAAI,QAAQ,OAAO;AAC3C,aAAW,CAAC,KAAK,KAAK,KAAK,gBAAgB,WAAW;AACpD,QAAI,GAAG,IAAI;AAAA,EACb;AACA,SAAO;AACT;AAWA,SAAS,kBAAkB,MAAkB;AAC3C,MAAI,gBAAgB,SAAS;AAC3B,WAAO;AAAA,EACT,WAAW,MAAM,QAAQ,IAAI,GAAG;AAC9B,WAAO,IAAI,QAAQ,IAAI;AAAA,EACzB,WAAW,OAAO,SAAS,UAAU;AACnC,WAAO,IAAI,QAAQ,IAAmB;AAAA,EACxC,OAAO;AACL,WAAO;AAAA,EACT;AACF;AAGO,SAAS,gBAAgB,SAA4B;AAC1D,SAAO,QAAQ,OAAO,CAAC,KAAc,WAAW;AAC9C,UAAM,kBAAkB,kBAAkB,MAAM;AAChD,QAAI,CAAC,gBAAiB,QAAO;AAC7B,eAAW,CAAC,KAAK,KAAK,KAAK,gBAAgB,WAAW;AACpD,UAAI,QAAQ,cAAc;AACxB,cAAM,eAAe,qBAAqB,KAAK;AAC/C,qBAAa,QAAQ,CAAC,WAAW,IAAI,OAAO,cAAc,MAAM,CAAC;AAAA,MACnE,OAAO;AACL,YAAI,IAAI,KAAK,KAAK;AAAA,MACpB;AAAA,IACF;AACA,WAAO;AAAA,EACT,GAAG,IAAI,SAAS;AAClB;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/router-core",
3
- "version": "1.157.10",
3
+ "version": "1.157.12",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
package/src/redirect.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { SAFE_URL_PROTOCOLS, isDangerousProtocol } from './utils'
2
2
  import type { NavigateOptions } from './link'
3
3
  import type { AnyRouter, RegisteredRouter } from './router'
4
+ import type { ParsedLocation } from './location'
4
5
 
5
6
  export type AnyRedirect = Redirect<any, any, any, any, any>
6
7
 
@@ -14,7 +15,13 @@ export type Redirect<
14
15
  TMaskFrom extends string = TFrom,
15
16
  TMaskTo extends string = '.',
16
17
  > = Response & {
17
- options: NavigateOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>
18
+ options: NavigateOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo> & {
19
+ /**
20
+ * @internal
21
+ * A **trusted** built location that can be used to redirect to.
22
+ */
23
+ _builtLocation?: ParsedLocation
24
+ }
18
25
  redirectHandled?: boolean
19
26
  }
20
27
 
@@ -45,6 +52,11 @@ export type RedirectOptions<
45
52
  * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RedirectType#headers-property)
46
53
  */
47
54
  headers?: HeadersInit
55
+ /**
56
+ * @internal
57
+ * A **trusted** built location that can be used to redirect to.
58
+ */
59
+ _builtLocation?: ParsedLocation
48
60
  } & NavigateOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>
49
61
 
50
62
  export type ResolvedRedirect<
@@ -113,13 +125,21 @@ export function redirect<
113
125
  opts.statusCode = opts.statusCode || opts.code || 307
114
126
 
115
127
  // Block dangerous protocols in redirect href
116
- if (typeof opts.href === 'string' && isDangerousProtocol(opts.href)) {
128
+ if (
129
+ !opts._builtLocation &&
130
+ typeof opts.href === 'string' &&
131
+ isDangerousProtocol(opts.href)
132
+ ) {
117
133
  throw new Error(
118
134
  `Redirect blocked: unsafe protocol in href "${opts.href}". Only ${SAFE_URL_PROTOCOLS.join(', ')} protocols are allowed.`,
119
135
  )
120
136
  }
121
137
 
122
- if (!opts.reloadDocument && typeof opts.href === 'string') {
138
+ if (
139
+ !opts._builtLocation &&
140
+ !opts.reloadDocument &&
141
+ typeof opts.href === 'string'
142
+ ) {
123
143
  try {
124
144
  new URL(opts.href)
125
145
  opts.reloadDocument = true
package/src/router.ts CHANGED
@@ -2284,8 +2284,11 @@ export class RouterCore<
2284
2284
  // always uses this.origin when constructing URLs
2285
2285
  if (this.latestLocation.publicHref !== nextLocation.publicHref) {
2286
2286
  const href = this.getParsedLocationHref(nextLocation)
2287
-
2288
- throw redirect({ href })
2287
+ if (nextLocation.external) {
2288
+ throw redirect({ href })
2289
+ } else {
2290
+ throw redirect({ href, _builtLocation: nextLocation })
2291
+ }
2289
2292
  }
2290
2293
  }
2291
2294
 
@@ -2614,8 +2617,9 @@ export class RouterCore<
2614
2617
  resolveRedirect = (redirect: AnyRedirect): AnyRedirect => {
2615
2618
  const locationHeader = redirect.headers.get('Location')
2616
2619
 
2617
- if (!redirect.options.href) {
2618
- const location = this.buildLocation(redirect.options)
2620
+ if (!redirect.options.href || redirect.options._builtLocation) {
2621
+ const location =
2622
+ redirect.options._builtLocation ?? this.buildLocation(redirect.options)
2619
2623
  const href = this.getParsedLocationHref(location)
2620
2624
  redirect.options.href = href
2621
2625
  redirect.headers.set('Location', href)
@@ -6,6 +6,7 @@ import {
6
6
  getOrigin,
7
7
  } from './ssr-server'
8
8
  import type { HandlerCallback } from './handlerCallback'
9
+ import type { AnyHeaders } from './headers'
9
10
  import type { AnyRouter } from '../router'
10
11
  import type { Manifest } from '../manifest'
11
12
 
@@ -77,21 +78,20 @@ export function createRequestHandler<TRouter extends AnyRouter>({
77
78
  }
78
79
 
79
80
  function getRequestHeaders(opts: { router: AnyRouter }): Headers {
80
- let headers = mergeHeaders(
81
- {
82
- 'Content-Type': 'text/html; charset=UTF-8',
83
- },
84
- ...opts.router.state.matches.map((match) => {
85
- return match.headers
86
- }),
81
+ const matchHeaders = opts.router.state.matches.map<AnyHeaders>(
82
+ (match) => match.headers,
87
83
  )
88
84
 
89
85
  // Handle Redirects
90
86
  const { redirect } = opts.router.state
91
-
92
87
  if (redirect) {
93
- headers = mergeHeaders(headers, redirect.headers)
88
+ matchHeaders.push(redirect.headers)
94
89
  }
95
90
 
96
- return headers
91
+ return mergeHeaders(
92
+ {
93
+ 'Content-Type': 'text/html; charset=UTF-8',
94
+ },
95
+ ...matchHeaders,
96
+ )
97
97
  }
@@ -13,7 +13,7 @@ export function headersInitToObject(
13
13
  return obj
14
14
  }
15
15
 
16
- type AnyHeaders =
16
+ export type AnyHeaders =
17
17
  | Headers
18
18
  | HeadersInit
19
19
  | Record<string, string>
@@ -24,13 +24,13 @@ type AnyHeaders =
24
24
  // Helper function to convert various HeaderInit types to a Headers instance
25
25
  function toHeadersInstance(init: AnyHeaders) {
26
26
  if (init instanceof Headers) {
27
- return new Headers(init)
27
+ return init
28
28
  } else if (Array.isArray(init)) {
29
29
  return new Headers(init)
30
30
  } else if (typeof init === 'object') {
31
31
  return new Headers(init as HeadersInit)
32
32
  } else {
33
- return new Headers()
33
+ return null
34
34
  }
35
35
  }
36
36
 
@@ -38,6 +38,7 @@ function toHeadersInstance(init: AnyHeaders) {
38
38
  export function mergeHeaders(...headers: Array<AnyHeaders>) {
39
39
  return headers.reduce((acc: Headers, header) => {
40
40
  const headersInstance = toHeadersInstance(header)
41
+ if (!headersInstance) return acc
41
42
  for (const [key, value] of headersInstance.entries()) {
42
43
  if (key === 'set-cookie') {
43
44
  const splitCookies = splitSetCookieString(value)