@tanstack/router-core 1.171.4 → 1.171.6

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.
Files changed (49) hide show
  1. package/dist/cjs/index.cjs +5 -1
  2. package/dist/cjs/index.d.cts +2 -2
  3. package/dist/cjs/manifest.cjs +43 -17
  4. package/dist/cjs/manifest.cjs.map +1 -1
  5. package/dist/cjs/manifest.d.cts +76 -24
  6. package/dist/cjs/router.cjs.map +1 -1
  7. package/dist/cjs/router.d.cts +2 -2
  8. package/dist/cjs/scroll-restoration-inline.cjs +1 -1
  9. package/dist/cjs/scroll-restoration-inline.cjs.map +1 -1
  10. package/dist/cjs/scroll-restoration-inline.d.cts +1 -4
  11. package/dist/cjs/scroll-restoration-script/server.cjs +5 -8
  12. package/dist/cjs/scroll-restoration-script/server.cjs.map +1 -1
  13. package/dist/cjs/scroll-restoration.cjs +32 -32
  14. package/dist/cjs/scroll-restoration.cjs.map +1 -1
  15. package/dist/cjs/ssr/createRequestHandler.cjs +2 -1
  16. package/dist/cjs/ssr/createRequestHandler.cjs.map +1 -1
  17. package/dist/cjs/ssr/createRequestHandler.d.cts +2 -2
  18. package/dist/cjs/ssr/ssr-server.cjs +132 -83
  19. package/dist/cjs/ssr/ssr-server.cjs.map +1 -1
  20. package/dist/cjs/ssr/ssr-server.d.cts +4 -5
  21. package/dist/esm/index.d.ts +2 -2
  22. package/dist/esm/index.js +2 -2
  23. package/dist/esm/manifest.d.ts +76 -24
  24. package/dist/esm/manifest.js +39 -17
  25. package/dist/esm/manifest.js.map +1 -1
  26. package/dist/esm/router.d.ts +2 -2
  27. package/dist/esm/router.js.map +1 -1
  28. package/dist/esm/scroll-restoration-inline.d.ts +1 -4
  29. package/dist/esm/scroll-restoration-inline.js +1 -1
  30. package/dist/esm/scroll-restoration-inline.js.map +1 -1
  31. package/dist/esm/scroll-restoration-script/server.js +5 -8
  32. package/dist/esm/scroll-restoration-script/server.js.map +1 -1
  33. package/dist/esm/scroll-restoration.js +32 -32
  34. package/dist/esm/scroll-restoration.js.map +1 -1
  35. package/dist/esm/ssr/createRequestHandler.d.ts +2 -2
  36. package/dist/esm/ssr/createRequestHandler.js +2 -1
  37. package/dist/esm/ssr/createRequestHandler.js.map +1 -1
  38. package/dist/esm/ssr/ssr-server.d.ts +4 -5
  39. package/dist/esm/ssr/ssr-server.js +133 -84
  40. package/dist/esm/ssr/ssr-server.js.map +1 -1
  41. package/package.json +1 -1
  42. package/src/index.ts +21 -1
  43. package/src/manifest.ts +151 -59
  44. package/src/router.ts +6 -4
  45. package/src/scroll-restoration-inline.ts +19 -38
  46. package/src/scroll-restoration-script/server.ts +5 -17
  47. package/src/scroll-restoration.ts +35 -40
  48. package/src/ssr/createRequestHandler.ts +6 -5
  49. package/src/ssr/ssr-server.ts +239 -141
@@ -10,7 +10,7 @@ import { AnyContext, AnyRoute, AnyRouteWithContext, LoaderStaleReloadMode, MakeR
10
10
  import { FullSearchSchema, RouteById, RoutePaths, RoutesById, RoutesByPath } from './routeInfo.cjs';
11
11
  import { AnyRouteMatch, MakeRouteMatchUnion, MatchRouteOptions } from './Matches.cjs';
12
12
  import { BuildLocationFn, CommitLocationOptions, NavigateFn } from './RouterProvider.cjs';
13
- import { Manifest, RouterManagedTag } from './manifest.cjs';
13
+ import { Manifest, ManifestRouteAssets, RouterManagedTag } from './manifest.cjs';
14
14
  import { AnySchema } from './validators.cjs';
15
15
  import { NavigateOptions, ResolveRelativePath, ToOptions } from './link.cjs';
16
16
  import { AnySerializationAdapter, ValidateSerializableInput } from './ssr/serializer/transformer.cjs';
@@ -522,7 +522,7 @@ export interface ServerSsr {
522
522
  cleanup: () => void;
523
523
  onSerializationFinished: (listener: () => void) => void;
524
524
  dehydrate: (opts?: {
525
- requestAssets?: Array<RouterManagedTag>;
525
+ requestAssets?: ManifestRouteAssets;
526
526
  }) => Promise<void>;
527
527
  takeBufferedScripts: () => RouterManagedTag | undefined;
528
528
  /**
@@ -1,5 +1,5 @@
1
1
  //#region src/scroll-restoration-inline.ts?script-string
2
- var scroll_restoration_inline_default = "function(i){let l;try{l=JSON.parse(sessionStorage.getItem(i.storageKey)||\"{}\")}catch(e){console.error(e);return}const c=i.key||window.history.state?.__TSR_key,o=c?l[c]:void 0;let f=!1;if(o&&typeof o==\"object\")for(const e in o){const t=o[e];if(!t||typeof t!=\"object\")continue;const r=t.scrollX,s=t.scrollY;if(!(!Number.isFinite(r)||!Number.isFinite(s))){if(e===\"window\")window.scrollTo({top:s,left:r}),f=!0;else if(e){let n;try{n=document.querySelector(e)}catch{continue}n&&(n.scrollLeft=r,n.scrollTop=s)}}}if(f)return;const w=window.location.hash.split(\"#\",2)[1];if(w){const e=window.history.state?.__hashScrollIntoViewOptions??!0;if(e){const t=document.getElementById(w);t&&t.scrollIntoView(e)}return}window.scrollTo({top:0,left:0})}";
2
+ var scroll_restoration_inline_default = "function(a,f){let l;try{l=JSON.parse(sessionStorage.getItem(a)||\"{}\")}catch{return}const n=l?.[f||history.state?.__TSR_key];let c=!1;for(const t in n){const e=n[t],o=e?.scrollX,s=e?.scrollY;if(Number.isFinite(o)&&Number.isFinite(s)){if(t===\"window\")scrollTo(o,s),c=!0;else if(t)try{const r=document.querySelector(t);r&&(r.scrollLeft=o,r.scrollTop=s)}catch{}}}if(c)return;const i=location.hash.slice(1);if(i){const t=history.state?.__hashScrollIntoViewOptions??!0;if(t){const e=document.getElementById(i);e&&e.scrollIntoView(t)}return}scrollTo(0,0)}";
3
3
  //#endregion
4
4
  exports.default = scroll_restoration_inline_default;
5
5
 
@@ -1 +1 @@
1
- {"version":3,"file":"scroll-restoration-inline.cjs","names":[],"sources":["../../src/scroll-restoration-inline.ts?script-string"],"sourcesContent":["export default function (options: { storageKey: string; key?: string }) {\n let byKey\n\n try {\n byKey = JSON.parse(sessionStorage.getItem(options.storageKey) || '{}')\n } catch (error) {\n console.error(error)\n return\n }\n\n const resolvedKey = options.key || window.history.state?.__TSR_key\n const elementEntries = resolvedKey ? byKey[resolvedKey] : undefined\n let windowRestored = false\n\n if (elementEntries && typeof elementEntries === 'object') {\n for (const elementSelector in elementEntries) {\n const entry = elementEntries[elementSelector]\n\n if (!entry || typeof entry !== 'object') {\n continue\n }\n\n const scrollX = entry.scrollX\n const scrollY = entry.scrollY\n\n if (!Number.isFinite(scrollX) || !Number.isFinite(scrollY)) {\n continue\n }\n\n if (elementSelector === 'window') {\n window.scrollTo({\n top: scrollY,\n left: scrollX,\n })\n windowRestored = true\n } else if (elementSelector) {\n let element\n\n try {\n element = document.querySelector(elementSelector)\n } catch {\n continue\n }\n\n if (element) {\n element.scrollLeft = scrollX\n element.scrollTop = scrollY\n }\n }\n }\n }\n\n if (windowRestored) return\n\n const hash = window.location.hash.split('#', 2)[1]\n\n if (hash) {\n const hashScrollIntoViewOptions =\n window.history.state?.__hashScrollIntoViewOptions ?? true\n\n if (hashScrollIntoViewOptions) {\n const el = document.getElementById(hash)\n if (el) {\n el.scrollIntoView(hashScrollIntoViewOptions)\n }\n }\n\n return\n }\n\n window.scrollTo({ top: 0, left: 0 })\n}\n"],"mappings":";AAAA,IAAA,oCAAe"}
1
+ {"version":3,"file":"scroll-restoration-inline.cjs","names":[],"sources":["../../src/scroll-restoration-inline.ts?script-string"],"sourcesContent":["export default function (storageKey: string, key?: string) {\n let byKey\n\n try {\n byKey = JSON.parse(sessionStorage.getItem(storageKey) || '{}')\n } catch {\n return\n }\n\n const elementEntries = byKey?.[key || history.state?.__TSR_key]\n let windowRestored = false\n\n for (const elementSelector in elementEntries) {\n const entry = elementEntries[elementSelector]\n const scrollX = entry?.scrollX\n const scrollY = entry?.scrollY\n\n if (Number.isFinite(scrollX) && Number.isFinite(scrollY)) {\n if (elementSelector === 'window') {\n scrollTo(scrollX, scrollY)\n windowRestored = true\n } else if (elementSelector) {\n try {\n const element = document.querySelector(elementSelector)\n if (element) {\n element.scrollLeft = scrollX\n element.scrollTop = scrollY\n }\n } catch {}\n }\n }\n }\n\n if (windowRestored) return\n\n const hash = location.hash.slice(1)\n\n if (hash) {\n const hashScrollIntoViewOptions =\n history.state?.__hashScrollIntoViewOptions ?? true\n\n if (hashScrollIntoViewOptions) {\n const el = document.getElementById(hash)\n if (el) {\n el.scrollIntoView(hashScrollIntoViewOptions)\n }\n }\n\n return\n }\n\n scrollTo(0, 0)\n}\n"],"mappings":";AAAA,IAAA,oCAAe"}
@@ -1,4 +1 @@
1
- export default function (options: {
2
- storageKey: string;
3
- key?: string;
4
- }): void;
1
+ export default function (storageKey: string, key?: string): void;
@@ -3,10 +3,10 @@ const require_utils = require("../utils.cjs");
3
3
  const require_scroll_restoration = require("../scroll-restoration.cjs");
4
4
  const require_scroll_restoration_inline = require("../scroll-restoration-inline.cjs");
5
5
  //#region src/scroll-restoration-script/server.ts
6
- const defaultInlineScrollRestorationScript = `(${require_scroll_restoration_inline.default})(${require_utils.escapeHtml(JSON.stringify({ storageKey: require_scroll_restoration.storageKey }))})`;
7
- function getScrollRestorationScript(options) {
8
- if (options.storageKey === "tsr-scroll-restoration-v1_3" && options.key === void 0) return defaultInlineScrollRestorationScript;
9
- return `(${require_scroll_restoration_inline.default})(${require_utils.escapeHtml(JSON.stringify(options))})`;
6
+ const defaultInlineScrollRestorationScript = `(${require_scroll_restoration_inline.default})(${require_utils.escapeHtml(JSON.stringify(require_scroll_restoration.storageKey))})`;
7
+ function getScrollRestorationScript(key) {
8
+ if (key === void 0) return defaultInlineScrollRestorationScript;
9
+ return `(${require_scroll_restoration_inline.default})(${require_utils.escapeHtml(JSON.stringify(require_scroll_restoration.storageKey))},${require_utils.escapeHtml(JSON.stringify(key))})`;
10
10
  }
11
11
  function getScrollRestorationScriptForRouter(router) {
12
12
  if (typeof router.options.scrollRestoration === "function" && !router.options.scrollRestoration({ location: router.latestLocation })) return null;
@@ -15,10 +15,7 @@ function getScrollRestorationScriptForRouter(router) {
15
15
  const location = router.latestLocation;
16
16
  const userKey = getKey(location);
17
17
  if (userKey === require_scroll_restoration.defaultGetScrollRestorationKey(location)) return defaultInlineScrollRestorationScript;
18
- return getScrollRestorationScript({
19
- storageKey: require_scroll_restoration.storageKey,
20
- key: userKey
21
- });
18
+ return getScrollRestorationScript(userKey);
22
19
  }
23
20
  //#endregion
24
21
  exports.getScrollRestorationScriptForRouter = getScrollRestorationScriptForRouter;
@@ -1 +1 @@
1
- {"version":3,"file":"server.cjs","names":[],"sources":["../../../src/scroll-restoration-script/server.ts"],"sourcesContent":["import minifiedScrollRestorationScript from '../scroll-restoration-inline?script-string'\nimport {\n defaultGetScrollRestorationKey,\n storageKey,\n} from '../scroll-restoration'\nimport { escapeHtml } from '../utils'\nimport type { AnyRouter } from '../router'\n\ntype InlineScrollRestorationScriptOptions = {\n storageKey: string\n key?: string\n}\n\nconst defaultInlineScrollRestorationScript = `(${minifiedScrollRestorationScript})(${escapeHtml(\n JSON.stringify({\n storageKey,\n } satisfies InlineScrollRestorationScriptOptions),\n)})`\n\nfunction getScrollRestorationScript(\n options: InlineScrollRestorationScriptOptions,\n) {\n if (options.storageKey === storageKey && options.key === undefined) {\n return defaultInlineScrollRestorationScript\n }\n\n return `(${minifiedScrollRestorationScript})(${escapeHtml(JSON.stringify(options))})`\n}\n\nexport function getScrollRestorationScriptForRouter(router: AnyRouter) {\n if (\n typeof router.options.scrollRestoration === 'function' &&\n !router.options.scrollRestoration({ location: router.latestLocation })\n ) {\n return null\n }\n\n const getKey = router.options.getScrollRestorationKey\n if (!getKey) {\n return defaultInlineScrollRestorationScript\n }\n\n const location = router.latestLocation\n const userKey = getKey(location)\n const defaultKey = defaultGetScrollRestorationKey(location)\n\n if (userKey === defaultKey) {\n return defaultInlineScrollRestorationScript\n }\n\n return getScrollRestorationScript({\n storageKey,\n key: userKey,\n })\n}\n"],"mappings":";;;;;AAaA,MAAM,uCAAuC,IAAI,kCAAA,QAAgC,IAAI,cAAA,WACnF,KAAK,UAAU,EACb,YAAA,2BAAA,YACD,CAAgD,CAClD,CAAC;AAEF,SAAS,2BACP,SACA;AACA,KAAI,QAAQ,eAAA,iCAA6B,QAAQ,QAAQ,KAAA,EACvD,QAAO;AAGT,QAAO,IAAI,kCAAA,QAAgC,IAAI,cAAA,WAAW,KAAK,UAAU,QAAQ,CAAC,CAAC;;AAGrF,SAAgB,oCAAoC,QAAmB;AACrE,KACE,OAAO,OAAO,QAAQ,sBAAsB,cAC5C,CAAC,OAAO,QAAQ,kBAAkB,EAAE,UAAU,OAAO,gBAAgB,CAAC,CAEtE,QAAO;CAGT,MAAM,SAAS,OAAO,QAAQ;AAC9B,KAAI,CAAC,OACH,QAAO;CAGT,MAAM,WAAW,OAAO;CACxB,MAAM,UAAU,OAAO,SAAS;AAGhC,KAAI,YAFe,2BAAA,+BAA+B,SAAS,CAGzD,QAAO;AAGT,QAAO,2BAA2B;EAChC,YAAA,2BAAA;EACA,KAAK;EACN,CAAC"}
1
+ {"version":3,"file":"server.cjs","names":[],"sources":["../../../src/scroll-restoration-script/server.ts"],"sourcesContent":["import minifiedScrollRestorationScript from '../scroll-restoration-inline?script-string'\nimport {\n defaultGetScrollRestorationKey,\n storageKey,\n} from '../scroll-restoration'\nimport { escapeHtml } from '../utils'\nimport type { AnyRouter } from '../router'\n\nconst defaultInlineScrollRestorationScript = `(${minifiedScrollRestorationScript})(${escapeHtml(\n JSON.stringify(storageKey),\n)})`\n\nfunction getScrollRestorationScript(key?: string) {\n if (key === undefined) {\n return defaultInlineScrollRestorationScript\n }\n\n return `(${minifiedScrollRestorationScript})(${escapeHtml(JSON.stringify(storageKey))},${escapeHtml(JSON.stringify(key))})`\n}\n\nexport function getScrollRestorationScriptForRouter(router: AnyRouter) {\n if (\n typeof router.options.scrollRestoration === 'function' &&\n !router.options.scrollRestoration({ location: router.latestLocation })\n ) {\n return null\n }\n\n const getKey = router.options.getScrollRestorationKey\n if (!getKey) {\n return defaultInlineScrollRestorationScript\n }\n\n const location = router.latestLocation\n const userKey = getKey(location)\n const defaultKey = defaultGetScrollRestorationKey(location)\n\n if (userKey === defaultKey) {\n return defaultInlineScrollRestorationScript\n }\n\n return getScrollRestorationScript(userKey)\n}\n"],"mappings":";;;;;AAQA,MAAM,uCAAuC,IAAI,kCAAA,QAAgC,IAAI,cAAA,WACnF,KAAK,UAAU,2BAAA,WAAW,CAC3B,CAAC;AAEF,SAAS,2BAA2B,KAAc;AAChD,KAAI,QAAQ,KAAA,EACV,QAAO;AAGT,QAAO,IAAI,kCAAA,QAAgC,IAAI,cAAA,WAAW,KAAK,UAAU,2BAAA,WAAW,CAAC,CAAC,GAAG,cAAA,WAAW,KAAK,UAAU,IAAI,CAAC,CAAC;;AAG3H,SAAgB,oCAAoC,QAAmB;AACrE,KACE,OAAO,OAAO,QAAQ,sBAAsB,cAC5C,CAAC,OAAO,QAAQ,kBAAkB,EAAE,UAAU,OAAO,gBAAgB,CAAC,CAEtE,QAAO;CAGT,MAAM,SAAS,OAAO,QAAQ;AAC9B,KAAI,CAAC,OACH,QAAO;CAGT,MAAM,WAAW,OAAO;CACxB,MAAM,UAAU,OAAO,SAAS;AAGhC,KAAI,YAFe,2BAAA,+BAA+B,SAAS,CAGzD,QAAO;AAGT,QAAO,2BAA2B,QAAQ"}
@@ -142,47 +142,47 @@ function setupScrollRestoration(router, force) {
142
142
  }
143
143
  }
144
144
  }
145
- if (!shouldResetScroll) return;
146
145
  ignoreScroll = true;
147
146
  try {
148
147
  const hash = event.toLocation.hash;
149
148
  const hashScrollIntoViewOptions = event.toLocation.state.__hashScrollIntoViewOptions ?? true;
150
- const action = require_router.locationHistoryActions.get(event.toLocation);
151
- const skipWindowRestore = hash && hashScrollIntoViewOptions && (action === "PUSH" || action === "REPLACE");
152
- const elementEntries = router.isScrollRestoring ? scrollRestorationCache[cacheKey] : void 0;
153
149
  let windowRestored = false;
154
- if (elementEntries) for (const elementSelector in elementEntries) {
155
- const { scrollX, scrollY } = elementEntries[elementSelector];
156
- if (elementSelector === windowScrollTarget) {
157
- if (skipWindowRestore) continue;
158
- scrollTo({
159
- top: scrollY,
160
- left: scrollX,
161
- behavior
162
- });
163
- windowRestored = true;
164
- } else {
165
- const element = getElement(elementSelector);
166
- if (element) {
167
- element.scrollLeft = scrollX;
168
- element.scrollTop = scrollY;
150
+ if (shouldResetScroll) {
151
+ const action = require_router.locationHistoryActions.get(event.toLocation);
152
+ const skipWindowRestore = hash && hashScrollIntoViewOptions && (action === "PUSH" || action === "REPLACE");
153
+ const elementEntries = router.isScrollRestoring ? scrollRestorationCache[cacheKey] : void 0;
154
+ if (elementEntries) for (const elementSelector in elementEntries) {
155
+ const { scrollX, scrollY } = elementEntries[elementSelector];
156
+ if (elementSelector === windowScrollTarget) {
157
+ if (skipWindowRestore) continue;
158
+ scrollTo({
159
+ top: scrollY,
160
+ left: scrollX,
161
+ behavior
162
+ });
163
+ windowRestored = true;
164
+ } else {
165
+ const element = getElement(elementSelector);
166
+ if (element) {
167
+ element.scrollLeft = scrollX;
168
+ element.scrollTop = scrollY;
169
+ }
169
170
  }
170
171
  }
171
- }
172
- if (!windowRestored) if (hash) {
173
- if (hashScrollIntoViewOptions) document.getElementById(hash)?.scrollIntoView(hashScrollIntoViewOptions);
174
- } else {
175
- const scrollOptions = {
176
- top: 0,
177
- left: 0,
178
- behavior
179
- };
180
- scrollTo(scrollOptions);
181
- if (scrollToTopSelectors) {
182
- scrollToTopElements ??= getScrollToTopElements(scrollToTopSelectors);
183
- for (const element of scrollToTopElements) element.scrollTo(scrollOptions);
172
+ if (!windowRestored && !hash) {
173
+ const scrollOptions = {
174
+ top: 0,
175
+ left: 0,
176
+ behavior
177
+ };
178
+ scrollTo(scrollOptions);
179
+ if (scrollToTopSelectors) {
180
+ scrollToTopElements ??= getScrollToTopElements(scrollToTopSelectors);
181
+ for (const element of scrollToTopElements) element.scrollTo(scrollOptions);
182
+ }
184
183
  }
185
184
  }
185
+ if (!windowRestored && hash && hashScrollIntoViewOptions) document.getElementById(hash)?.scrollIntoView(hashScrollIntoViewOptions);
186
186
  } finally {
187
187
  ignoreScroll = false;
188
188
  }
@@ -1 +1 @@
1
- {"version":3,"file":"scroll-restoration.cjs","names":[],"sources":["../../src/scroll-restoration.ts"],"sourcesContent":["import { isServer } from '@tanstack/router-core/isServer'\nimport { locationHistoryActions } from './router'\nimport type { AnyRouter } from './router'\nimport type { ParsedLocation } from './location'\n\nexport type ScrollRestorationEntry = { scrollX: number; scrollY: number }\n\ntype ScrollRestorationByElement = Record<string, ScrollRestorationEntry>\n\ntype ScrollRestorationByKey = Record<string, ScrollRestorationByElement>\n\nexport type ScrollRestorationOptions = {\n getKey?: (location: ParsedLocation) => string\n scrollBehavior?: ScrollToOptions['behavior']\n}\n\nfunction getSafeSessionStorage() {\n try {\n // Accessing sessionStorage itself can throw SecurityError in locked-down\n // contexts, e.g. sandboxed/opaque origins or blocked storage policies.\n return sessionStorage\n } catch {\n return\n }\n}\n\n// SessionStorage key used to store scroll positions across navigations.\nexport const storageKey = 'tsr-scroll-restoration-v1_3'\nconst safeSessionStorage = getSafeSessionStorage()\n\nfunction createScrollRestorationCache() {\n try {\n return JSON.parse(\n safeSessionStorage?.getItem(storageKey) || '{}',\n ) as ScrollRestorationByKey\n } catch {\n // ignore invalid session storage payloads\n return {}\n }\n}\n\nfunction persistScrollRestorationCache() {\n try {\n safeSessionStorage?.setItem(\n storageKey,\n JSON.stringify(scrollRestorationCache),\n )\n } catch {\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n '[ts-router] Could not persist scroll restoration state to sessionStorage.',\n )\n }\n }\n}\n\nconst scrollRestorationCache = /* @__PURE__ */ createScrollRestorationCache()\nconst scrollRestorationIdAttribute = 'data-scroll-restoration-id'\n\n/**\n * The default `getKey` function for `useScrollRestoration`.\n * It returns the `key` from the location state or the `href` of the location.\n *\n * The `location.href` is used as a fallback to support the use case where the location state is not available like the initial render.\n */\nexport const defaultGetScrollRestorationKey = (location: ParsedLocation) => {\n return location.state.__TSR_key! || location.href\n}\n\nfunction getScrollRestorationSelector(element: Element): string {\n const attrId = element.getAttribute(scrollRestorationIdAttribute)\n if (attrId) {\n return `[${scrollRestorationIdAttribute}=\"${attrId}\"]`\n }\n\n let selector = ''\n let el: any = element\n let parent: HTMLElement\n\n while ((parent = el.parentNode)) {\n let index = 1\n let sibling = el\n while ((sibling = sibling.previousElementSibling)) {\n index++\n }\n\n const part = `${el.localName}:nth-child(${index})`\n selector = selector ? `${part} > ${selector}` : part\n el = parent\n }\n\n return selector\n}\n\nexport function getElementScrollRestorationEntry(\n router: AnyRouter,\n options: (\n | {\n id: string\n getElement?: () => Window | Element | undefined | null\n }\n | {\n id?: string\n getElement: () => Window | Element | undefined | null\n }\n ) & {\n getKey?: (location: ParsedLocation) => string\n },\n): ScrollRestorationEntry | undefined {\n const getKey = options.getKey || defaultGetScrollRestorationKey\n const restoreKey = getKey(router.latestLocation)\n const entries = scrollRestorationCache[restoreKey]\n\n if (!entries) {\n return\n }\n\n if (options.id) {\n return entries[`[${scrollRestorationIdAttribute}=\"${options.id}\"]`]\n }\n\n const element = options.getElement?.()\n if (!element) {\n return\n }\n\n return entries[\n element === window\n ? windowScrollTarget\n : getScrollRestorationSelector(element as Element)\n ]\n}\n\nlet ignoreScroll = false\nconst windowScrollTarget = 'window'\ntype ScrollTarget = typeof windowScrollTarget | Element\n\nfunction getElement(selector: string | (() => Element | null | undefined)) {\n try {\n return typeof selector === 'function'\n ? selector()\n : document.querySelector(selector)\n } catch {}\n return\n}\n\nfunction getScrollToTopElements(\n scrollToTopSelectors: NonNullable<\n AnyRouter['options']['scrollToTopSelectors']\n >,\n): Array<Element> {\n const elements: Array<Element> = []\n\n for (const selector of scrollToTopSelectors) {\n if (selector === windowScrollTarget) {\n continue\n }\n\n const element = getElement(selector)\n if (element) {\n elements.push(element)\n }\n }\n\n return elements\n}\n\nexport function setupScrollRestoration(router: AnyRouter, force?: boolean) {\n // Keep hash/top scrolling active even when sessionStorage is unavailable.\n\n if (force ?? router.options.scrollRestoration) {\n router.isScrollRestoring = true\n }\n\n if ((isServer ?? router.isServer) || router.isScrollRestorationSetup) {\n return\n }\n\n router.isScrollRestorationSetup = true\n ignoreScroll = false\n\n const getKey =\n router.options.getScrollRestorationKey || defaultGetScrollRestorationKey\n const trackedScrollEntries = new Map<ScrollTarget, ScrollRestorationEntry>()\n const setTrackedScrollEntry = (\n target: ScrollTarget,\n scrollX: number,\n scrollY: number,\n ) => {\n const entry =\n trackedScrollEntries.get(target) || ({} as ScrollRestorationEntry)\n entry.scrollX = scrollX\n entry.scrollY = scrollY\n trackedScrollEntries.set(target, entry)\n }\n\n history.scrollRestoration = 'manual'\n\n const onScroll = (event: Event) => {\n if (ignoreScroll || !router.isScrollRestoring) {\n return\n }\n\n if (event.target === document) {\n setTrackedScrollEntry(windowScrollTarget, scrollX, scrollY)\n } else {\n const target = event.target as Element\n setTrackedScrollEntry(target, target.scrollLeft, target.scrollTop)\n }\n }\n\n // Snapshot the current page's tracked scroll targets before navigation or unload.\n const snapshotCurrentScrollTargets = (restoreKey: string) => {\n if (!router.isScrollRestoring) {\n return\n }\n\n const keyEntry = (scrollRestorationCache[restoreKey] ||=\n {} as ScrollRestorationByElement)\n\n for (const [target, position] of trackedScrollEntries) {\n if (target === windowScrollTarget) {\n keyEntry[windowScrollTarget] = position\n } else if (target.isConnected) {\n keyEntry[getScrollRestorationSelector(target)] = position\n }\n }\n }\n\n document.addEventListener('scroll', onScroll, true)\n router.subscribe('onBeforeLoad', (event) => {\n if (event.fromLocation) {\n snapshotCurrentScrollTargets(getKey(event.fromLocation))\n }\n trackedScrollEntries.clear()\n })\n addEventListener('pagehide', () => {\n snapshotCurrentScrollTargets(\n getKey(\n router.stores.resolvedLocation.get() ?? router.stores.location.get(),\n ),\n )\n persistScrollRestorationCache()\n })\n\n // Restore destination scroll after the new route has rendered.\n router.subscribe('onRendered', (event) => {\n const behavior = router.options.scrollRestorationBehavior\n const scrollToTopSelectors = router.options.scrollToTopSelectors\n const shouldResetScroll = router.resetNextScroll\n let scrollToTopElements: Array<Element> | undefined\n trackedScrollEntries.clear()\n\n if (!shouldResetScroll) {\n router.resetNextScroll = true\n }\n\n if (\n typeof router.options.scrollRestoration === 'function' &&\n !router.options.scrollRestoration({ location: router.latestLocation })\n ) {\n return\n }\n\n const cacheKey = getKey(event.toLocation)\n const fromCacheKey = event.fromLocation && getKey(event.fromLocation)\n\n if (router.isScrollRestoring && fromCacheKey && fromCacheKey !== cacheKey) {\n const fromElementEntries = scrollRestorationCache[fromCacheKey]\n\n if (fromElementEntries) {\n let toElementEntries = scrollRestorationCache[cacheKey]\n\n for (const elementSelector in fromElementEntries) {\n if (elementSelector === windowScrollTarget) {\n if (shouldResetScroll) {\n continue\n }\n } else {\n const element = getElement(elementSelector)\n if (!element) {\n continue\n }\n\n if (shouldResetScroll && scrollToTopSelectors) {\n scrollToTopElements ??=\n getScrollToTopElements(scrollToTopSelectors)\n if (scrollToTopElements.includes(element)) {\n continue\n }\n }\n }\n\n if (!toElementEntries) {\n toElementEntries = scrollRestorationCache[cacheKey] =\n {} as ScrollRestorationByElement\n }\n\n toElementEntries[elementSelector] ??=\n fromElementEntries[elementSelector]!\n }\n }\n }\n\n if (!shouldResetScroll) {\n return\n }\n\n ignoreScroll = true\n\n try {\n const hash = event.toLocation.hash\n const hashScrollIntoViewOptions =\n event.toLocation.state.__hashScrollIntoViewOptions ?? true\n const action = locationHistoryActions.get(event.toLocation)\n const skipWindowRestore =\n hash &&\n hashScrollIntoViewOptions &&\n (action === 'PUSH' || action === 'REPLACE')\n\n const elementEntries = router.isScrollRestoring\n ? scrollRestorationCache[cacheKey]\n : undefined\n let windowRestored = false\n\n if (elementEntries) {\n for (const elementSelector in elementEntries) {\n const { scrollX, scrollY } = elementEntries[elementSelector]!\n\n if (elementSelector === windowScrollTarget) {\n if (skipWindowRestore) {\n continue\n }\n\n scrollTo({\n top: scrollY,\n left: scrollX,\n behavior,\n })\n windowRestored = true\n } else {\n const element = getElement(elementSelector)\n if (element) {\n element.scrollLeft = scrollX\n element.scrollTop = scrollY\n }\n }\n }\n }\n\n if (!windowRestored) {\n if (hash) {\n if (hashScrollIntoViewOptions) {\n document\n .getElementById(hash)\n ?.scrollIntoView(hashScrollIntoViewOptions)\n }\n } else {\n const scrollOptions = {\n top: 0,\n left: 0,\n behavior,\n }\n\n scrollTo(scrollOptions)\n if (scrollToTopSelectors) {\n scrollToTopElements ??= getScrollToTopElements(scrollToTopSelectors)\n for (const element of scrollToTopElements) {\n element.scrollTo(scrollOptions)\n }\n }\n }\n }\n } finally {\n ignoreScroll = false\n }\n })\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"scroll-restoration.cjs","names":[],"sources":["../../src/scroll-restoration.ts"],"sourcesContent":["import { isServer } from '@tanstack/router-core/isServer'\nimport { locationHistoryActions } from './router'\nimport type { AnyRouter } from './router'\nimport type { ParsedLocation } from './location'\n\nexport type ScrollRestorationEntry = { scrollX: number; scrollY: number }\n\ntype ScrollRestorationByElement = Record<string, ScrollRestorationEntry>\n\ntype ScrollRestorationByKey = Record<string, ScrollRestorationByElement>\n\nexport type ScrollRestorationOptions = {\n getKey?: (location: ParsedLocation) => string\n scrollBehavior?: ScrollToOptions['behavior']\n}\n\nfunction getSafeSessionStorage() {\n try {\n // Accessing sessionStorage itself can throw SecurityError in locked-down\n // contexts, e.g. sandboxed/opaque origins or blocked storage policies.\n return sessionStorage\n } catch {\n return\n }\n}\n\n// SessionStorage key used to store scroll positions across navigations.\nexport const storageKey = 'tsr-scroll-restoration-v1_3'\nconst safeSessionStorage = getSafeSessionStorage()\n\nfunction createScrollRestorationCache() {\n try {\n return JSON.parse(\n safeSessionStorage?.getItem(storageKey) || '{}',\n ) as ScrollRestorationByKey\n } catch {\n // ignore invalid session storage payloads\n return {}\n }\n}\n\nfunction persistScrollRestorationCache() {\n try {\n safeSessionStorage?.setItem(\n storageKey,\n JSON.stringify(scrollRestorationCache),\n )\n } catch {\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n '[ts-router] Could not persist scroll restoration state to sessionStorage.',\n )\n }\n }\n}\n\nconst scrollRestorationCache = /* @__PURE__ */ createScrollRestorationCache()\nconst scrollRestorationIdAttribute = 'data-scroll-restoration-id'\n\n/**\n * The default `getKey` function for `useScrollRestoration`.\n * It returns the `key` from the location state or the `href` of the location.\n *\n * The `location.href` is used as a fallback to support the use case where the location state is not available like the initial render.\n */\nexport const defaultGetScrollRestorationKey = (location: ParsedLocation) => {\n return location.state.__TSR_key! || location.href\n}\n\nfunction getScrollRestorationSelector(element: Element): string {\n const attrId = element.getAttribute(scrollRestorationIdAttribute)\n if (attrId) {\n return `[${scrollRestorationIdAttribute}=\"${attrId}\"]`\n }\n\n let selector = ''\n let el: any = element\n let parent: HTMLElement\n\n while ((parent = el.parentNode)) {\n let index = 1\n let sibling = el\n while ((sibling = sibling.previousElementSibling)) {\n index++\n }\n\n const part = `${el.localName}:nth-child(${index})`\n selector = selector ? `${part} > ${selector}` : part\n el = parent\n }\n\n return selector\n}\n\nexport function getElementScrollRestorationEntry(\n router: AnyRouter,\n options: (\n | {\n id: string\n getElement?: () => Window | Element | undefined | null\n }\n | {\n id?: string\n getElement: () => Window | Element | undefined | null\n }\n ) & {\n getKey?: (location: ParsedLocation) => string\n },\n): ScrollRestorationEntry | undefined {\n const getKey = options.getKey || defaultGetScrollRestorationKey\n const restoreKey = getKey(router.latestLocation)\n const entries = scrollRestorationCache[restoreKey]\n\n if (!entries) {\n return\n }\n\n if (options.id) {\n return entries[`[${scrollRestorationIdAttribute}=\"${options.id}\"]`]\n }\n\n const element = options.getElement?.()\n if (!element) {\n return\n }\n\n return entries[\n element === window\n ? windowScrollTarget\n : getScrollRestorationSelector(element as Element)\n ]\n}\n\nlet ignoreScroll = false\nconst windowScrollTarget = 'window'\ntype ScrollTarget = typeof windowScrollTarget | Element\n\nfunction getElement(selector: string | (() => Element | null | undefined)) {\n try {\n return typeof selector === 'function'\n ? selector()\n : document.querySelector(selector)\n } catch {}\n return\n}\n\nfunction getScrollToTopElements(\n scrollToTopSelectors: NonNullable<\n AnyRouter['options']['scrollToTopSelectors']\n >,\n): Array<Element> {\n const elements: Array<Element> = []\n\n for (const selector of scrollToTopSelectors) {\n if (selector === windowScrollTarget) {\n continue\n }\n\n const element = getElement(selector)\n if (element) {\n elements.push(element)\n }\n }\n\n return elements\n}\n\nexport function setupScrollRestoration(router: AnyRouter, force?: boolean) {\n // Keep hash/top scrolling active even when sessionStorage is unavailable.\n\n if (force ?? router.options.scrollRestoration) {\n router.isScrollRestoring = true\n }\n\n if ((isServer ?? router.isServer) || router.isScrollRestorationSetup) {\n return\n }\n\n router.isScrollRestorationSetup = true\n ignoreScroll = false\n\n const getKey =\n router.options.getScrollRestorationKey || defaultGetScrollRestorationKey\n const trackedScrollEntries = new Map<ScrollTarget, ScrollRestorationEntry>()\n const setTrackedScrollEntry = (\n target: ScrollTarget,\n scrollX: number,\n scrollY: number,\n ) => {\n const entry =\n trackedScrollEntries.get(target) || ({} as ScrollRestorationEntry)\n entry.scrollX = scrollX\n entry.scrollY = scrollY\n trackedScrollEntries.set(target, entry)\n }\n\n history.scrollRestoration = 'manual'\n\n const onScroll = (event: Event) => {\n if (ignoreScroll || !router.isScrollRestoring) {\n return\n }\n\n if (event.target === document) {\n setTrackedScrollEntry(windowScrollTarget, scrollX, scrollY)\n } else {\n const target = event.target as Element\n setTrackedScrollEntry(target, target.scrollLeft, target.scrollTop)\n }\n }\n\n // Snapshot the current page's tracked scroll targets before navigation or unload.\n const snapshotCurrentScrollTargets = (restoreKey: string) => {\n if (!router.isScrollRestoring) {\n return\n }\n\n const keyEntry = (scrollRestorationCache[restoreKey] ||=\n {} as ScrollRestorationByElement)\n\n for (const [target, position] of trackedScrollEntries) {\n if (target === windowScrollTarget) {\n keyEntry[windowScrollTarget] = position\n } else if (target.isConnected) {\n keyEntry[getScrollRestorationSelector(target)] = position\n }\n }\n }\n\n document.addEventListener('scroll', onScroll, true)\n router.subscribe('onBeforeLoad', (event) => {\n if (event.fromLocation) {\n snapshotCurrentScrollTargets(getKey(event.fromLocation))\n }\n trackedScrollEntries.clear()\n })\n addEventListener('pagehide', () => {\n snapshotCurrentScrollTargets(\n getKey(\n router.stores.resolvedLocation.get() ?? router.stores.location.get(),\n ),\n )\n persistScrollRestorationCache()\n })\n\n // Restore destination scroll after the new route has rendered.\n router.subscribe('onRendered', (event) => {\n const behavior = router.options.scrollRestorationBehavior\n const scrollToTopSelectors = router.options.scrollToTopSelectors\n const shouldResetScroll = router.resetNextScroll\n let scrollToTopElements: Array<Element> | undefined\n trackedScrollEntries.clear()\n\n if (!shouldResetScroll) {\n router.resetNextScroll = true\n }\n\n if (\n typeof router.options.scrollRestoration === 'function' &&\n !router.options.scrollRestoration({ location: router.latestLocation })\n ) {\n return\n }\n\n const cacheKey = getKey(event.toLocation)\n const fromCacheKey = event.fromLocation && getKey(event.fromLocation)\n\n if (router.isScrollRestoring && fromCacheKey && fromCacheKey !== cacheKey) {\n const fromElementEntries = scrollRestorationCache[fromCacheKey]\n\n if (fromElementEntries) {\n let toElementEntries = scrollRestorationCache[cacheKey]\n\n for (const elementSelector in fromElementEntries) {\n if (elementSelector === windowScrollTarget) {\n if (shouldResetScroll) {\n continue\n }\n } else {\n const element = getElement(elementSelector)\n if (!element) {\n continue\n }\n\n if (shouldResetScroll && scrollToTopSelectors) {\n scrollToTopElements ??=\n getScrollToTopElements(scrollToTopSelectors)\n if (scrollToTopElements.includes(element)) {\n continue\n }\n }\n }\n\n if (!toElementEntries) {\n toElementEntries = scrollRestorationCache[cacheKey] =\n {} as ScrollRestorationByElement\n }\n\n toElementEntries[elementSelector] ??=\n fromElementEntries[elementSelector]!\n }\n }\n }\n\n ignoreScroll = true\n\n try {\n const hash = event.toLocation.hash\n const hashScrollIntoViewOptions =\n event.toLocation.state.__hashScrollIntoViewOptions ?? true\n let windowRestored = false\n\n if (shouldResetScroll) {\n const action = locationHistoryActions.get(event.toLocation)\n const skipWindowRestore =\n hash &&\n hashScrollIntoViewOptions &&\n (action === 'PUSH' || action === 'REPLACE')\n\n const elementEntries = router.isScrollRestoring\n ? scrollRestorationCache[cacheKey]\n : undefined\n\n if (elementEntries) {\n for (const elementSelector in elementEntries) {\n const { scrollX, scrollY } = elementEntries[elementSelector]!\n\n if (elementSelector === windowScrollTarget) {\n if (skipWindowRestore) {\n continue\n }\n\n scrollTo({\n top: scrollY,\n left: scrollX,\n behavior,\n })\n windowRestored = true\n } else {\n const element = getElement(elementSelector)\n if (element) {\n element.scrollLeft = scrollX\n element.scrollTop = scrollY\n }\n }\n }\n }\n\n if (!windowRestored && !hash) {\n const scrollOptions = {\n top: 0,\n left: 0,\n behavior,\n }\n\n scrollTo(scrollOptions)\n if (scrollToTopSelectors) {\n scrollToTopElements ??= getScrollToTopElements(scrollToTopSelectors)\n for (const element of scrollToTopElements) {\n element.scrollTo(scrollOptions)\n }\n }\n }\n }\n\n if (!windowRestored && hash && hashScrollIntoViewOptions) {\n document.getElementById(hash)?.scrollIntoView(hashScrollIntoViewOptions)\n }\n } finally {\n ignoreScroll = false\n }\n })\n}\n"],"mappings":"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"}
@@ -33,7 +33,8 @@ function createRequestHandler({ createRouter, request, getRouterManifest }) {
33
33
  };
34
34
  }
35
35
  function getRequestHeaders(opts) {
36
- const matchHeaders = opts.router.stores.matches.get().map((match) => match.headers);
36
+ const matchHeaders = [];
37
+ for (const match of opts.router.stores.matches.get()) matchHeaders.push(match.headers);
37
38
  const redirect = opts.router.stores.redirect.get();
38
39
  if (redirect) matchHeaders.push(redirect.headers);
39
40
  return require_headers.mergeHeaders({ "Content-Type": "text/html; charset=UTF-8" }, ...matchHeaders);
@@ -1 +1 @@
1
- {"version":3,"file":"createRequestHandler.cjs","names":[],"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.stores.matches\n .get()\n .map<AnyHeaders>((match) => match.headers)\n\n // Handle Redirects\n const redirect = opts.router.stores.redirect.get()\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"],"mappings":";;;;AAgBA,SAAgB,qBAAgD,EAC9D,cACA,SACA,qBAK0B;AAC1B,QAAO,OAAO,OAAO;EACnB,MAAM,SAAS,cAAc;EAE7B,IAAI,gBAAgB;AAEpB,MAAI;AACF,sBAAA,2BAA2B;IACzB;IACA,UAAU,MAAM,qBAAqB;IACtC,CAAC;GAGF,MAAM,EAAE,QAAQ,mBAAA,iBAAiB,QAAQ,KAAK,mBAAmB;GACjE,MAAM,SAAS,mBAAA,UAAU,QAAQ;GAIjC,MAAM,WAAA,GAAA,kBAAA,qBAA8B,EAClC,gBAAgB,CAJL,IAAI,KAAK,QAAQ,IAAI,QAAQ,GAAG,CAIrB,EACvB,CAAC;AAGF,UAAO,OAAO;IACZ;IACA,QAAQ,OAAO,QAAQ,UAAU;IAClC,CAAC;AAEF,SAAM,OAAO,MAAM;AAEnB,SAAM,OAAO,WAAW,WAAW;GAEnC,MAAM,kBAAkB,kBAAkB,EACxC,QACD,CAAC;AAGF,mBAAgB;AAChB,UAAO,GAAG;IACR;IACA;IACA;IACD,CAAC;YACM;AACR,OAAI,CAAC,cAKH,QAAO,WAAW,SAAS;;;;AAMnC,SAAS,kBAAkB,MAAsC;CAC/D,MAAM,eAAe,KAAK,OAAO,OAAO,QACrC,KAAK,CACL,KAAiB,UAAU,MAAM,QAAQ;CAG5C,MAAM,WAAW,KAAK,OAAO,OAAO,SAAS,KAAK;AAClD,KAAI,SACF,cAAa,KAAK,SAAS,QAAQ;AAGrC,QAAO,gBAAA,aACL,EACE,gBAAgB,4BACjB,EACD,GAAG,aACJ"}
1
+ {"version":3,"file":"createRequestHandler.cjs","names":[],"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 { ServerManifest } 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?: () => ServerManifest | Promise<ServerManifest>\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: Array<AnyHeaders> = []\n for (const match of opts.router.stores.matches.get()) {\n matchHeaders.push(match.headers)\n }\n\n // Handle Redirects\n const redirect = opts.router.stores.redirect.get()\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"],"mappings":";;;;AAgBA,SAAgB,qBAAgD,EAC9D,cACA,SACA,qBAK0B;AAC1B,QAAO,OAAO,OAAO;EACnB,MAAM,SAAS,cAAc;EAE7B,IAAI,gBAAgB;AAEpB,MAAI;AACF,sBAAA,2BAA2B;IACzB;IACA,UAAU,MAAM,qBAAqB;IACtC,CAAC;GAGF,MAAM,EAAE,QAAQ,mBAAA,iBAAiB,QAAQ,KAAK,mBAAmB;GACjE,MAAM,SAAS,mBAAA,UAAU,QAAQ;GAIjC,MAAM,WAAA,GAAA,kBAAA,qBAA8B,EAClC,gBAAgB,CAJL,IAAI,KAAK,QAAQ,IAAI,QAAQ,GAAG,CAIrB,EACvB,CAAC;AAGF,UAAO,OAAO;IACZ;IACA,QAAQ,OAAO,QAAQ,UAAU;IAClC,CAAC;AAEF,SAAM,OAAO,MAAM;AAEnB,SAAM,OAAO,WAAW,WAAW;GAEnC,MAAM,kBAAkB,kBAAkB,EACxC,QACD,CAAC;AAGF,mBAAgB;AAChB,UAAO,GAAG;IACR;IACA;IACA;IACD,CAAC;YACM;AACR,OAAI,CAAC,cAKH,QAAO,WAAW,SAAS;;;;AAMnC,SAAS,kBAAkB,MAAsC;CAC/D,MAAM,eAAkC,EAAE;AAC1C,MAAK,MAAM,SAAS,KAAK,OAAO,OAAO,QAAQ,KAAK,CAClD,cAAa,KAAK,MAAM,QAAQ;CAIlC,MAAM,WAAW,KAAK,OAAO,OAAO,SAAS,KAAK;AAClD,KAAI,SACF,cAAa,KAAK,SAAS,QAAQ;AAGrC,QAAO,gBAAA,aACL,EACE,gBAAgB,4BACjB,EACD,GAAG,aACJ"}
@@ -1,9 +1,9 @@
1
1
  import { HandlerCallback } from './handlerCallback.cjs';
2
2
  import { AnyRouter } from '../router.cjs';
3
- import { Manifest } from '../manifest.cjs';
3
+ import { ServerManifest } from '../manifest.cjs';
4
4
  export type RequestHandler<TRouter extends AnyRouter> = (cb: HandlerCallback<TRouter>) => Promise<Response>;
5
5
  export declare function createRequestHandler<TRouter extends AnyRouter>({ createRouter, request, getRouterManifest, }: {
6
6
  createRouter: () => TRouter;
7
7
  request: Request;
8
- getRouterManifest?: () => Manifest | Promise<Manifest>;
8
+ getRouterManifest?: () => ServerManifest | Promise<ServerManifest>;
9
9
  }): RequestHandler<TRouter>;
@@ -96,7 +96,6 @@ var ScriptBuffer = class {
96
96
  const isProd = process.env.NODE_ENV === "production";
97
97
  const MANIFEST_CACHE_SIZE = 100;
98
98
  const manifestCaches = /* @__PURE__ */ new WeakMap();
99
- const inlineCssCaches = /* @__PURE__ */ new WeakMap();
100
99
  function getManifestCache(manifest) {
101
100
  const cache = manifestCaches.get(manifest);
102
101
  if (cache) return cache;
@@ -104,83 +103,141 @@ function getManifestCache(manifest) {
104
103
  manifestCaches.set(manifest, newCache);
105
104
  return newCache;
106
105
  }
107
- function getInlineCssCache(manifest) {
108
- const cache = inlineCssCaches.get(manifest);
109
- if (cache) return cache;
110
- const newCache = require_lru_cache.createLRUCache(MANIFEST_CACHE_SIZE);
111
- inlineCssCaches.set(manifest, newCache);
112
- return newCache;
106
+ function getInlineCssForPreparedRoutes(manifest, preparedRoutes) {
107
+ if (preparedRoutes.inlineCss !== void 0) return preparedRoutes.inlineCss;
108
+ const styles = manifest.inlineCss?.styles;
109
+ const hrefs = preparedRoutes.inlineCssHrefs;
110
+ if (!styles || !hrefs?.length) return void 0;
111
+ let css = "";
112
+ for (const href of hrefs) css += styles[href];
113
+ preparedRoutes.inlineCss = css;
114
+ return css;
113
115
  }
114
- function getInlineCssHrefsForMatches(manifest, matches) {
115
- const styles = manifest?.inlineCss?.styles;
116
- if (!styles) return [];
117
- const seen = /* @__PURE__ */ new Set();
118
- const hrefs = [];
119
- for (const match of matches) {
120
- const assets = manifest?.routes[match.routeId]?.assets ?? [];
121
- for (const asset of assets) {
122
- const href = require_manifest.getStylesheetHref(asset);
123
- if (!href || seen.has(href) || styles[href] === void 0) continue;
124
- seen.add(href);
125
- hrefs.push(href);
126
- }
127
- }
128
- return hrefs;
116
+ function getInlineCssAssetForPreparedRoutes(manifest, preparedRoutes) {
117
+ const css = getInlineCssForPreparedRoutes(manifest, preparedRoutes);
118
+ return css === void 0 ? void 0 : require_manifest.createInlineCssStyleAsset(css);
129
119
  }
130
- function getInlineCssForHrefs(manifest, hrefs) {
131
- const styles = manifest.inlineCss?.styles;
132
- if (!styles || hrefs.length === 0) return void 0;
133
- const cacheKey = hrefs.join("\0");
120
+ function getMatchedRoutesCacheKey(matches) {
121
+ let cacheKey = "";
122
+ for (let i = 0; i < matches.length; i++) cacheKey += (i === 0 ? "" : "\0") + matches[i].routeId;
123
+ return cacheKey;
124
+ }
125
+ function getPreparedMatchedManifestRoutes(manifest, matches, cacheKey) {
134
126
  if (isProd) {
135
- const cached = getInlineCssCache(manifest).get(cacheKey);
136
- if (cached !== void 0) return cached;
127
+ const cached = getManifestCache(manifest).get(cacheKey);
128
+ if (cached) return cached;
137
129
  }
138
- const css = hrefs.map((href) => styles[href]).join("");
139
- if (isProd) getInlineCssCache(manifest).set(cacheKey, css);
140
- return css;
130
+ const preparedRoutes = prepareMatchedManifestRoutes(manifest, matches);
131
+ if (isProd) getManifestCache(manifest).set(cacheKey, preparedRoutes);
132
+ return preparedRoutes;
141
133
  }
142
- function getInlineCssAssetForMatches(manifest, matches) {
143
- if (!manifest?.inlineCss) return void 0;
144
- const css = getInlineCssForHrefs(manifest, getInlineCssHrefsForMatches(manifest, matches));
145
- return css === void 0 ? void 0 : require_manifest.createInlineCssStyleAsset(css);
134
+ function prepareMatchedManifestRoutes(manifest, matches) {
135
+ const inlineStyles = manifest.inlineCss?.styles;
136
+ const routes = {};
137
+ if (!inlineStyles) {
138
+ for (const match of matches) {
139
+ const route = manifest.routes[match.routeId];
140
+ if (route) routes[match.routeId] = route;
141
+ }
142
+ return {
143
+ routes,
144
+ hasStrippedRoutes: false
145
+ };
146
+ }
147
+ const inlineCssHrefs = [];
148
+ const seenInlineCssHrefs = /* @__PURE__ */ new Set();
149
+ let hasStrippedRoutes = false;
150
+ for (const match of matches) {
151
+ const routeId = match.routeId;
152
+ const route = manifest.routes[routeId];
153
+ if (!route) continue;
154
+ const nextRoute = stripInlinedStylesheetAssetsFromRoute(inlineStyles, route, inlineCssHrefs, seenInlineCssHrefs);
155
+ if (nextRoute !== route) hasStrippedRoutes = true;
156
+ routes[routeId] = nextRoute;
157
+ }
158
+ return {
159
+ routes,
160
+ hasStrippedRoutes,
161
+ ...inlineCssHrefs.length ? { inlineCssHrefs } : {}
162
+ };
146
163
  }
147
- function stripInlinedStylesheetAssets(manifest, routes, matches) {
148
- if (!manifest.inlineCss) return routes;
149
- const nextRoutes = {};
150
- for (const [routeId, route] of Object.entries(routes)) {
151
- const assets = route.assets?.filter((asset) => !require_manifest.isInlinableStylesheet(manifest, asset));
164
+ function stripInlinedStylesheetAssetsFromRoute(inlineStyles, route, inlineCssHrefs, seenInlineCssHrefs) {
165
+ const css = route.css;
166
+ if (!css) return route;
167
+ if (css.length === 0) {
152
168
  const nextRoute = { ...route };
153
- if (assets) if (assets.length > 0) nextRoute.assets = assets;
154
- else delete nextRoute.assets;
155
- nextRoutes[routeId] = nextRoute;
169
+ delete nextRoute.css;
170
+ return nextRoute;
156
171
  }
157
- if (getInlineCssAssetForMatches(manifest, matches)) {
158
- const rootRoute = nextRoutes["__root__"] ?? {};
159
- nextRoutes[require_root.rootRouteId] = {
160
- ...rootRoute,
161
- assets: [require_manifest.createInlineCssPlaceholderAsset(), ...rootRoute.assets ?? []]
162
- };
172
+ let cssLinks;
173
+ for (let i = 0; i < css.length; i++) {
174
+ const link = css[i];
175
+ const href = require_manifest.getStylesheetHref(link);
176
+ if (inlineStyles[href] === void 0) {
177
+ if (cssLinks) cssLinks.push(link);
178
+ continue;
179
+ }
180
+ if (!seenInlineCssHrefs.has(href)) {
181
+ seenInlineCssHrefs.add(href);
182
+ inlineCssHrefs.push(href);
183
+ }
184
+ if (!cssLinks) cssLinks = css.slice(0, i);
163
185
  }
164
- return nextRoutes;
186
+ if (!cssLinks) return route;
187
+ if (cssLinks.length > 0) return {
188
+ ...route,
189
+ css: cssLinks
190
+ };
191
+ const nextRoute = { ...route };
192
+ delete nextRoute.css;
193
+ return nextRoute;
194
+ }
195
+ function hasRouteAssets(route) {
196
+ return !!route.scripts?.length || !!route.css?.length;
197
+ }
198
+ function hasRequestAssets(assets) {
199
+ return !!assets && (!!assets.preloads?.length || hasRouteAssets(assets));
200
+ }
201
+ function mergeRequestAssetsIntoRootRoute(rootRoute, requestAssets) {
202
+ const preloads = requestAssets?.preloads?.length ? [...requestAssets.preloads, ...rootRoute?.preloads ?? []] : rootRoute?.preloads;
203
+ const scripts = requestAssets?.scripts?.length ? [...requestAssets.scripts, ...rootRoute?.scripts ?? []] : rootRoute?.scripts;
204
+ const cssLinks = requestAssets?.css?.length ? [...requestAssets.css, ...rootRoute?.css ?? []] : rootRoute?.css;
205
+ return {
206
+ ...rootRoute ?? {},
207
+ ...preloads?.length ? { preloads } : {},
208
+ ...scripts?.length ? { scripts } : {},
209
+ ...cssLinks?.length ? { css: cssLinks } : {}
210
+ };
165
211
  }
166
- function attachRouterServerSsrUtils({ router, manifest, getRequestAssets, includeUnmatchedRouteAssets = true }) {
212
+ function attachRouterServerSsrUtils({ router, manifest, getRequestAssets }) {
167
213
  router.ssr = { get manifest() {
168
214
  if (!manifest) return manifest;
169
215
  const requestAssets = getRequestAssets?.();
170
- const inlineCssAsset = getInlineCssAssetForMatches(manifest, router.stores.matches.get());
171
- if (!requestAssets?.length && !inlineCssAsset) return manifest;
216
+ const matches = router.stores.matches.get();
217
+ const hasAssets = hasRequestAssets(requestAssets);
218
+ if (!hasAssets && !manifest.inlineCss) return manifest;
219
+ let inlineCssAsset;
220
+ let routes = manifest.routes;
221
+ if (manifest.inlineCss) {
222
+ const preparedManifest = getPreparedMatchedManifestRoutes(manifest, matches, getMatchedRoutesCacheKey(matches));
223
+ inlineCssAsset = getInlineCssAssetForPreparedRoutes(manifest, preparedManifest);
224
+ if (preparedManifest.hasStrippedRoutes) routes = {
225
+ ...manifest.routes,
226
+ ...preparedManifest.routes
227
+ };
228
+ }
229
+ if (!hasAssets) return {
230
+ ...manifest.scriptFormat ? { scriptFormat: manifest.scriptFormat } : {},
231
+ ...inlineCssAsset ? { inlineStyle: inlineCssAsset } : {},
232
+ routes
233
+ };
234
+ const rootRoute = routes[require_root.rootRouteId];
172
235
  return {
173
- ...manifest,
236
+ ...manifest.scriptFormat ? { scriptFormat: manifest.scriptFormat } : {},
237
+ ...inlineCssAsset ? { inlineStyle: inlineCssAsset } : {},
174
238
  routes: {
175
- ...manifest.routes,
176
- [require_root.rootRouteId]: {
177
- ...manifest.routes[require_root.rootRouteId],
178
- assets: [
179
- ...requestAssets ?? [],
180
- ...inlineCssAsset ? [inlineCssAsset] : [],
181
- ...manifest.routes["__root__"]?.assets ?? []
182
- ]
183
- }
239
+ ...routes,
240
+ [require_root.rootRouteId]: mergeRequestAssetsIntoRootRoute(rootRoute, requestAssets)
184
241
  }
185
242
  };
186
243
  } };
@@ -211,27 +268,19 @@ function attachRouterServerSsrUtils({ router, manifest, getRequestAssets, includ
211
268
  const matches = matchesToDehydrate.map(dehydrateMatch);
212
269
  let manifestToDehydrate = void 0;
213
270
  if (manifest) {
214
- const currentRouteIdsList = matchesToDehydrate.map((m) => m.routeId);
215
- const manifestCacheKey = `${currentRouteIdsList.join("\0")}\0includeUnmatchedRouteAssets=${includeUnmatchedRouteAssets}`;
216
- let filteredRoutes;
217
- if (isProd) filteredRoutes = getManifestCache(manifest).get(manifestCacheKey);
218
- if (!filteredRoutes) {
219
- const currentRouteIds = new Set(currentRouteIdsList);
220
- const nextFilteredRoutes = {};
221
- for (const routeId in manifest.routes) {
222
- const routeManifest = manifest.routes[routeId];
223
- if (currentRouteIds.has(routeId)) nextFilteredRoutes[routeId] = routeManifest;
224
- else if (includeUnmatchedRouteAssets && routeManifest.assets && routeManifest.assets.length > 0) nextFilteredRoutes[routeId] = { assets: routeManifest.assets };
225
- }
226
- filteredRoutes = stripInlinedStylesheetAssets(manifest, nextFilteredRoutes, matchesToDehydrate);
227
- if (isProd) getManifestCache(manifest).set(manifestCacheKey, filteredRoutes);
228
- }
229
- manifestToDehydrate = { routes: { ...filteredRoutes } };
230
- if (opts?.requestAssets?.length) {
271
+ const cacheKey = getMatchedRoutesCacheKey(matchesToDehydrate);
272
+ const preparedManifest = getPreparedMatchedManifestRoutes(manifest, matchesToDehydrate, cacheKey);
273
+ manifestToDehydrate = {
274
+ ...manifest.scriptFormat ? { scriptFormat: manifest.scriptFormat } : {},
275
+ ...preparedManifest.inlineCssHrefs ? { inlineStyle: require_manifest.createInlineCssPlaceholderAsset() } : {},
276
+ routes: preparedManifest.routes
277
+ };
278
+ const requestAssets = opts?.requestAssets;
279
+ if (hasRequestAssets(requestAssets)) {
231
280
  const existingRoot = manifestToDehydrate.routes[require_root.rootRouteId];
232
- manifestToDehydrate.routes[require_root.rootRouteId] = {
233
- ...existingRoot,
234
- assets: [...opts.requestAssets, ...existingRoot?.assets ?? []]
281
+ manifestToDehydrate.routes = {
282
+ ...manifestToDehydrate.routes,
283
+ [require_root.rootRouteId]: mergeRequestAssetsIntoRootRoute(existingRoot, requestAssets)
235
284
  };
236
285
  }
237
286
  }