@tanstack/router-core 1.168.17 → 1.168.18

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 (85) hide show
  1. package/dist/cjs/Matches.cjs +1 -1
  2. package/dist/cjs/Matches.cjs.map +1 -1
  3. package/dist/cjs/config.cjs +1 -1
  4. package/dist/cjs/config.cjs.map +1 -1
  5. package/dist/cjs/defer.cjs +1 -1
  6. package/dist/cjs/defer.cjs.map +1 -1
  7. package/dist/cjs/isServer/client.cjs +1 -1
  8. package/dist/cjs/isServer/client.cjs.map +1 -1
  9. package/dist/cjs/isServer/development.cjs +1 -1
  10. package/dist/cjs/isServer/development.cjs.map +1 -1
  11. package/dist/cjs/isServer/server.cjs +1 -1
  12. package/dist/cjs/isServer/server.cjs.map +1 -1
  13. package/dist/cjs/link.cjs +1 -1
  14. package/dist/cjs/link.cjs.map +1 -1
  15. package/dist/cjs/load-matches.cjs +19 -19
  16. package/dist/cjs/load-matches.cjs.map +1 -1
  17. package/dist/cjs/new-process-route-tree.cjs +2 -2
  18. package/dist/cjs/new-process-route-tree.cjs.map +1 -1
  19. package/dist/cjs/root.cjs +1 -1
  20. package/dist/cjs/root.cjs.map +1 -1
  21. package/dist/cjs/router.cjs +2 -2
  22. package/dist/cjs/router.cjs.map +1 -1
  23. package/dist/cjs/scroll-restoration-script/server.cjs +1 -1
  24. package/dist/cjs/scroll-restoration-script/server.cjs.map +1 -1
  25. package/dist/cjs/scroll-restoration.cjs +6 -6
  26. package/dist/cjs/scroll-restoration.cjs.map +1 -1
  27. package/dist/cjs/searchParams.cjs +2 -2
  28. package/dist/cjs/searchParams.cjs.map +1 -1
  29. package/dist/cjs/ssr/constants.cjs +2 -2
  30. package/dist/cjs/ssr/constants.cjs.map +1 -1
  31. package/dist/cjs/ssr/serializer/RawStream.cjs +12 -12
  32. package/dist/cjs/ssr/serializer/RawStream.cjs.map +1 -1
  33. package/dist/cjs/ssr/serializer/ShallowErrorPlugin.cjs +1 -1
  34. package/dist/cjs/ssr/serializer/ShallowErrorPlugin.cjs.map +1 -1
  35. package/dist/cjs/ssr/serializer/seroval-plugins.cjs +1 -1
  36. package/dist/cjs/ssr/serializer/seroval-plugins.cjs.map +1 -1
  37. package/dist/cjs/ssr/ssr-server.cjs +9 -9
  38. package/dist/cjs/ssr/ssr-server.cjs.map +1 -1
  39. package/dist/cjs/ssr/transformStreamWithRouter.cjs +6 -6
  40. package/dist/cjs/ssr/transformStreamWithRouter.cjs.map +1 -1
  41. package/dist/cjs/utils.cjs +7 -7
  42. package/dist/cjs/utils.cjs.map +1 -1
  43. package/dist/esm/Matches.js +1 -1
  44. package/dist/esm/Matches.js.map +1 -1
  45. package/dist/esm/config.js +1 -1
  46. package/dist/esm/config.js.map +1 -1
  47. package/dist/esm/defer.js +1 -1
  48. package/dist/esm/defer.js.map +1 -1
  49. package/dist/esm/isServer/client.js +1 -1
  50. package/dist/esm/isServer/client.js.map +1 -1
  51. package/dist/esm/isServer/development.js +1 -1
  52. package/dist/esm/isServer/development.js.map +1 -1
  53. package/dist/esm/isServer/server.js +1 -1
  54. package/dist/esm/isServer/server.js.map +1 -1
  55. package/dist/esm/link.js +1 -1
  56. package/dist/esm/link.js.map +1 -1
  57. package/dist/esm/load-matches.js +19 -19
  58. package/dist/esm/load-matches.js.map +1 -1
  59. package/dist/esm/new-process-route-tree.js +2 -2
  60. package/dist/esm/new-process-route-tree.js.map +1 -1
  61. package/dist/esm/root.js +1 -1
  62. package/dist/esm/root.js.map +1 -1
  63. package/dist/esm/router.js +2 -2
  64. package/dist/esm/router.js.map +1 -1
  65. package/dist/esm/scroll-restoration-script/server.js +1 -1
  66. package/dist/esm/scroll-restoration-script/server.js.map +1 -1
  67. package/dist/esm/scroll-restoration.js +6 -6
  68. package/dist/esm/scroll-restoration.js.map +1 -1
  69. package/dist/esm/searchParams.js +2 -2
  70. package/dist/esm/searchParams.js.map +1 -1
  71. package/dist/esm/ssr/constants.js +2 -2
  72. package/dist/esm/ssr/constants.js.map +1 -1
  73. package/dist/esm/ssr/serializer/RawStream.js +10 -10
  74. package/dist/esm/ssr/serializer/RawStream.js.map +1 -1
  75. package/dist/esm/ssr/serializer/ShallowErrorPlugin.js +1 -1
  76. package/dist/esm/ssr/serializer/ShallowErrorPlugin.js.map +1 -1
  77. package/dist/esm/ssr/serializer/seroval-plugins.js +1 -1
  78. package/dist/esm/ssr/serializer/seroval-plugins.js.map +1 -1
  79. package/dist/esm/ssr/ssr-server.js +9 -9
  80. package/dist/esm/ssr/ssr-server.js.map +1 -1
  81. package/dist/esm/ssr/transformStreamWithRouter.js +6 -6
  82. package/dist/esm/ssr/transformStreamWithRouter.js.map +1 -1
  83. package/dist/esm/utils.js +7 -7
  84. package/dist/esm/utils.js.map +1 -1
  85. package/package.json +1 -1
@@ -2,7 +2,7 @@ import { escapeHtml } from "../utils.js";
2
2
  import { defaultGetScrollRestorationKey, storageKey } from "../scroll-restoration.js";
3
3
  import scroll_restoration_inline_default from "../scroll-restoration-inline.js";
4
4
  //#region src/scroll-restoration-script/server.ts
5
- var defaultInlineScrollRestorationScript = `(${scroll_restoration_inline_default})(${escapeHtml(JSON.stringify({
5
+ const defaultInlineScrollRestorationScript = `(${scroll_restoration_inline_default})(${escapeHtml(JSON.stringify({
6
6
  storageKey,
7
7
  shouldScrollRestoration: true
8
8
  }))})`;
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","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 behavior?: ScrollToOptions['behavior']\n shouldScrollRestoration?: boolean\n}\n\nconst defaultInlineScrollRestorationScript = `(${minifiedScrollRestorationScript})(${escapeHtml(\n JSON.stringify({\n storageKey,\n shouldScrollRestoration: true,\n } satisfies InlineScrollRestorationScriptOptions),\n)})`\n\nfunction getScrollRestorationScript(\n options: InlineScrollRestorationScriptOptions,\n) {\n if (\n options.storageKey === storageKey &&\n options.shouldScrollRestoration === true &&\n options.key === undefined &&\n options.behavior === undefined\n ) {\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 shouldScrollRestoration: true,\n key: userKey,\n })\n}\n"],"mappings":";;;;AAeA,IAAM,uCAAuC,IAAI,kCAAgC,IAAI,WACnF,KAAK,UAAU;CACb;CACA,yBAAyB;CAC1B,CAAgD,CAClD,CAAC;AAEF,SAAS,2BACP,SACA;AACA,KACE,QAAQ,eAAA,iCACR,QAAQ,4BAA4B,QACpC,QAAQ,QAAQ,KAAA,KAChB,QAAQ,aAAa,KAAA,EAErB,QAAO;AAGT,QAAO,IAAI,kCAAgC,IAAI,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,+BAA+B,SAAS,CAGzD,QAAO;AAGT,QAAO,2BAA2B;EAChC;EACA,yBAAyB;EACzB,KAAK;EACN,CAAC"}
1
+ {"version":3,"file":"server.js","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 behavior?: ScrollToOptions['behavior']\n shouldScrollRestoration?: boolean\n}\n\nconst defaultInlineScrollRestorationScript = `(${minifiedScrollRestorationScript})(${escapeHtml(\n JSON.stringify({\n storageKey,\n shouldScrollRestoration: true,\n } satisfies InlineScrollRestorationScriptOptions),\n)})`\n\nfunction getScrollRestorationScript(\n options: InlineScrollRestorationScriptOptions,\n) {\n if (\n options.storageKey === storageKey &&\n options.shouldScrollRestoration === true &&\n options.key === undefined &&\n options.behavior === undefined\n ) {\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 shouldScrollRestoration: true,\n key: userKey,\n })\n}\n"],"mappings":";;;;AAeA,MAAM,uCAAuC,IAAI,kCAAgC,IAAI,WACnF,KAAK,UAAU;CACb;CACA,yBAAyB;CAC1B,CAAgD,CAClD,CAAC;AAEF,SAAS,2BACP,SACA;AACA,KACE,QAAQ,eAAA,iCACR,QAAQ,4BAA4B,QACpC,QAAQ,QAAQ,KAAA,KAChB,QAAQ,aAAa,KAAA,EAErB,QAAO;AAGT,QAAO,IAAI,kCAAgC,IAAI,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,+BAA+B,SAAS,CAGzD,QAAO;AAGT,QAAO,2BAA2B;EAChC;EACA,yBAAyB;EACzB,KAAK;EACN,CAAC"}
@@ -8,7 +8,7 @@ function getSafeSessionStorage() {
8
8
  return;
9
9
  }
10
10
  }
11
- var storageKey = "tsr-scroll-restoration-v1_3";
11
+ const storageKey = "tsr-scroll-restoration-v1_3";
12
12
  function createScrollRestorationCache() {
13
13
  const safeSessionStorage = getSafeSessionStorage();
14
14
  if (!safeSessionStorage) return null;
@@ -34,14 +34,14 @@ function createScrollRestorationCache() {
34
34
  persist
35
35
  };
36
36
  }
37
- var scrollRestorationCache = createScrollRestorationCache();
37
+ const scrollRestorationCache = createScrollRestorationCache();
38
38
  /**
39
39
  * The default `getKey` function for `useScrollRestoration`.
40
40
  * It returns the `key` from the location state or the `href` of the location.
41
41
  *
42
42
  * The `location.href` is used as a fallback to support the use case where the location state is not available like the initial render.
43
43
  */
44
- var defaultGetScrollRestorationKey = (location) => {
44
+ const defaultGetScrollRestorationKey = (location) => {
45
45
  return location.state.__TSR_key || location.href;
46
46
  };
47
47
  function getCssSelector(el) {
@@ -60,9 +60,9 @@ function getElementScrollRestorationEntry(router, options) {
60
60
  if (!element) return;
61
61
  return scrollRestorationCache?.state[restoreKey]?.[element instanceof Window ? windowScrollTarget : getCssSelector(element)];
62
62
  }
63
- var ignoreScroll = false;
64
- var windowScrollTarget = "window";
65
- var scrollRestorationIdAttribute = "data-scroll-restoration-id";
63
+ let ignoreScroll = false;
64
+ const windowScrollTarget = "window";
65
+ const scrollRestorationIdAttribute = "data-scroll-restoration-id";
66
66
  function setupScrollRestoration(router, force) {
67
67
  if (!scrollRestorationCache && !(isServer ?? router.isServer)) return;
68
68
  const cache = scrollRestorationCache;
@@ -1 +1 @@
1
- {"version":3,"file":"scroll-restoration.js","names":[],"sources":["../../src/scroll-restoration.ts"],"sourcesContent":["import { isServer } from '@tanstack/router-core/isServer'\nimport { functionalUpdate, isPlainObject } from './utils'\nimport type { AnyRouter } from './router'\nimport type { ParsedLocation } from './location'\nimport type { NonNullableUpdater } from './utils'\n\nexport type ScrollRestorationEntry = { scrollX: number; scrollY: number }\n\ntype ScrollRestorationByElement = Record<string, ScrollRestorationEntry>\n\ntype ScrollRestorationByKey = Record<string, ScrollRestorationByElement>\n\ntype ScrollRestorationCache = {\n readonly state: ScrollRestorationByKey\n set: (updater: NonNullableUpdater<ScrollRestorationByKey>) => void\n persist: () => void\n}\n\nexport type ScrollRestorationOptions = {\n getKey?: (location: ParsedLocation) => string\n scrollBehavior?: ScrollToOptions['behavior']\n}\n\nfunction getSafeSessionStorage() {\n try {\n return typeof window !== 'undefined' &&\n typeof window.sessionStorage === 'object'\n ? window.sessionStorage\n : undefined\n } catch {\n // silent\n return undefined\n }\n}\n\n// SessionStorage key used to store scroll positions across navigations.\nexport const storageKey = 'tsr-scroll-restoration-v1_3'\n\nfunction createScrollRestorationCache(): ScrollRestorationCache | null {\n const safeSessionStorage = getSafeSessionStorage()\n if (!safeSessionStorage) {\n return null\n }\n\n let state: ScrollRestorationByKey = {}\n\n try {\n const parsed = JSON.parse(safeSessionStorage.getItem(storageKey) || '{}')\n if (isPlainObject(parsed)) {\n state = parsed as ScrollRestorationByKey\n }\n } catch {\n // ignore invalid session storage payloads\n }\n\n const persist = () => {\n try {\n safeSessionStorage.setItem(storageKey, JSON.stringify(state))\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\n return {\n get state() {\n return state\n },\n set: (updater) => {\n state = functionalUpdate(updater, state) || state\n },\n persist,\n }\n}\n\nexport const scrollRestorationCache = createScrollRestorationCache()\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 getCssSelector(el: any): string {\n const path = []\n let parent: HTMLElement\n while ((parent = el.parentNode)) {\n path.push(\n `${el.tagName}:nth-child(${Array.prototype.indexOf.call(parent.children, el) + 1})`,\n )\n el = parent\n }\n return `${path.reverse().join(' > ')}`.toLowerCase()\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\n if (options.id) {\n return scrollRestorationCache?.state[restoreKey]?.[\n `[${scrollRestorationIdAttribute}=\"${options.id}\"]`\n ]\n }\n\n const element = options.getElement?.()\n if (!element) {\n return\n }\n\n return scrollRestorationCache?.state[restoreKey]?.[\n element instanceof Window ? windowScrollTarget : getCssSelector(element)\n ]\n}\n\nlet ignoreScroll = false\nconst windowScrollTarget = 'window'\nconst scrollRestorationIdAttribute = 'data-scroll-restoration-id'\ntype ScrollTarget = typeof windowScrollTarget | Element\n\nexport function setupScrollRestoration(router: AnyRouter, force?: boolean) {\n if (!scrollRestorationCache && !(isServer ?? router.isServer)) {\n return\n }\n\n const cache = scrollRestorationCache\n\n const shouldScrollRestoration =\n force ?? router.options.scrollRestoration ?? false\n\n if (shouldScrollRestoration) {\n router.isScrollRestoring = true\n }\n\n if (\n (isServer ?? router.isServer) ||\n router.isScrollRestorationSetup ||\n !cache\n ) {\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\n window.history.scrollRestoration = 'manual'\n\n const onScroll = (event: Event) => {\n if (ignoreScroll || !router.isScrollRestoring) {\n return\n }\n\n if (event.target === document || event.target === window) {\n trackedScrollEntries.set(windowScrollTarget, {\n scrollX: window.scrollX || 0,\n scrollY: window.scrollY || 0,\n })\n } else {\n const target = event.target as Element\n trackedScrollEntries.set(target, {\n scrollX: target.scrollLeft || 0,\n scrollY: target.scrollTop || 0,\n })\n }\n }\n\n // Snapshot the current page's tracked scroll targets before navigation or unload.\n const snapshotCurrentScrollTargets = (restoreKey?: string) => {\n if (\n !router.isScrollRestoring ||\n !restoreKey ||\n trackedScrollEntries.size === 0 ||\n !cache\n ) {\n return\n }\n\n const keyEntry = (cache.state[restoreKey] ||=\n {} as ScrollRestorationByElement)\n\n for (const [target, position] of trackedScrollEntries) {\n let selector: string | undefined\n\n if (target === windowScrollTarget) {\n selector = windowScrollTarget\n } else if (target.isConnected) {\n const attrId = target.getAttribute(scrollRestorationIdAttribute)\n selector = attrId\n ? `[${scrollRestorationIdAttribute}=\"${attrId}\"]`\n : getCssSelector(target)\n }\n\n if (!selector) {\n continue\n }\n\n keyEntry[selector] = position\n }\n }\n\n document.addEventListener('scroll', onScroll, true)\n router.subscribe('onBeforeLoad', (event) => {\n snapshotCurrentScrollTargets(\n event.fromLocation ? getKey(event.fromLocation) : undefined,\n )\n trackedScrollEntries.clear()\n })\n window.addEventListener('pagehide', () => {\n snapshotCurrentScrollTargets(\n getKey(\n router.stores.resolvedLocation.get() ?? router.stores.location.get(),\n ),\n )\n cache.persist()\n })\n\n // Restore destination scroll after the new route has rendered.\n router.subscribe('onRendered', (event) => {\n const cacheKey = getKey(event.toLocation)\n const behavior = router.options.scrollRestorationBehavior\n const scrollToTopSelectors = router.options.scrollToTopSelectors\n trackedScrollEntries.clear()\n\n if (!router.resetNextScroll) {\n router.resetNextScroll = true\n return\n }\n\n if (\n typeof router.options.scrollRestoration === 'function' &&\n !router.options.scrollRestoration({ location: router.latestLocation })\n ) {\n return\n }\n\n ignoreScroll = true\n\n try {\n const elementEntries = router.isScrollRestoring\n ? cache.state[cacheKey]\n : undefined\n let restored = false\n\n if (elementEntries) {\n for (const elementSelector in elementEntries) {\n const entry = elementEntries[elementSelector]\n\n if (!isPlainObject(entry)) {\n continue\n }\n\n const { scrollX, scrollY } = entry as {\n scrollX?: unknown\n scrollY?: unknown\n }\n\n if (!Number.isFinite(scrollX) || !Number.isFinite(scrollY)) {\n continue\n }\n\n if (elementSelector === windowScrollTarget) {\n window.scrollTo({\n top: scrollY as number,\n left: scrollX as number,\n behavior,\n })\n restored = 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 as number\n element.scrollTop = scrollY as number\n restored = true\n }\n }\n }\n }\n\n if (!restored) {\n const hash = router.history.location.hash.slice(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 } else {\n const scrollOptions = {\n top: 0,\n left: 0,\n behavior,\n }\n\n window.scrollTo(scrollOptions)\n if (scrollToTopSelectors) {\n for (const selector of scrollToTopSelectors) {\n if (selector === windowScrollTarget) continue\n const element =\n typeof selector === 'function'\n ? selector()\n : document.querySelector(selector)\n if (element) {\n element.scrollTo(scrollOptions)\n }\n }\n }\n }\n }\n } finally {\n ignoreScroll = false\n }\n\n if (router.isScrollRestoring) {\n cache.set((state) => {\n state[cacheKey] ||= {} as ScrollRestorationByElement\n return state\n })\n }\n })\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"scroll-restoration.js","names":[],"sources":["../../src/scroll-restoration.ts"],"sourcesContent":["import { isServer } from '@tanstack/router-core/isServer'\nimport { functionalUpdate, isPlainObject } from './utils'\nimport type { AnyRouter } from './router'\nimport type { ParsedLocation } from './location'\nimport type { NonNullableUpdater } from './utils'\n\nexport type ScrollRestorationEntry = { scrollX: number; scrollY: number }\n\ntype ScrollRestorationByElement = Record<string, ScrollRestorationEntry>\n\ntype ScrollRestorationByKey = Record<string, ScrollRestorationByElement>\n\ntype ScrollRestorationCache = {\n readonly state: ScrollRestorationByKey\n set: (updater: NonNullableUpdater<ScrollRestorationByKey>) => void\n persist: () => void\n}\n\nexport type ScrollRestorationOptions = {\n getKey?: (location: ParsedLocation) => string\n scrollBehavior?: ScrollToOptions['behavior']\n}\n\nfunction getSafeSessionStorage() {\n try {\n return typeof window !== 'undefined' &&\n typeof window.sessionStorage === 'object'\n ? window.sessionStorage\n : undefined\n } catch {\n // silent\n return undefined\n }\n}\n\n// SessionStorage key used to store scroll positions across navigations.\nexport const storageKey = 'tsr-scroll-restoration-v1_3'\n\nfunction createScrollRestorationCache(): ScrollRestorationCache | null {\n const safeSessionStorage = getSafeSessionStorage()\n if (!safeSessionStorage) {\n return null\n }\n\n let state: ScrollRestorationByKey = {}\n\n try {\n const parsed = JSON.parse(safeSessionStorage.getItem(storageKey) || '{}')\n if (isPlainObject(parsed)) {\n state = parsed as ScrollRestorationByKey\n }\n } catch {\n // ignore invalid session storage payloads\n }\n\n const persist = () => {\n try {\n safeSessionStorage.setItem(storageKey, JSON.stringify(state))\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\n return {\n get state() {\n return state\n },\n set: (updater) => {\n state = functionalUpdate(updater, state) || state\n },\n persist,\n }\n}\n\nexport const scrollRestorationCache = createScrollRestorationCache()\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 getCssSelector(el: any): string {\n const path = []\n let parent: HTMLElement\n while ((parent = el.parentNode)) {\n path.push(\n `${el.tagName}:nth-child(${Array.prototype.indexOf.call(parent.children, el) + 1})`,\n )\n el = parent\n }\n return `${path.reverse().join(' > ')}`.toLowerCase()\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\n if (options.id) {\n return scrollRestorationCache?.state[restoreKey]?.[\n `[${scrollRestorationIdAttribute}=\"${options.id}\"]`\n ]\n }\n\n const element = options.getElement?.()\n if (!element) {\n return\n }\n\n return scrollRestorationCache?.state[restoreKey]?.[\n element instanceof Window ? windowScrollTarget : getCssSelector(element)\n ]\n}\n\nlet ignoreScroll = false\nconst windowScrollTarget = 'window'\nconst scrollRestorationIdAttribute = 'data-scroll-restoration-id'\ntype ScrollTarget = typeof windowScrollTarget | Element\n\nexport function setupScrollRestoration(router: AnyRouter, force?: boolean) {\n if (!scrollRestorationCache && !(isServer ?? router.isServer)) {\n return\n }\n\n const cache = scrollRestorationCache\n\n const shouldScrollRestoration =\n force ?? router.options.scrollRestoration ?? false\n\n if (shouldScrollRestoration) {\n router.isScrollRestoring = true\n }\n\n if (\n (isServer ?? router.isServer) ||\n router.isScrollRestorationSetup ||\n !cache\n ) {\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\n window.history.scrollRestoration = 'manual'\n\n const onScroll = (event: Event) => {\n if (ignoreScroll || !router.isScrollRestoring) {\n return\n }\n\n if (event.target === document || event.target === window) {\n trackedScrollEntries.set(windowScrollTarget, {\n scrollX: window.scrollX || 0,\n scrollY: window.scrollY || 0,\n })\n } else {\n const target = event.target as Element\n trackedScrollEntries.set(target, {\n scrollX: target.scrollLeft || 0,\n scrollY: target.scrollTop || 0,\n })\n }\n }\n\n // Snapshot the current page's tracked scroll targets before navigation or unload.\n const snapshotCurrentScrollTargets = (restoreKey?: string) => {\n if (\n !router.isScrollRestoring ||\n !restoreKey ||\n trackedScrollEntries.size === 0 ||\n !cache\n ) {\n return\n }\n\n const keyEntry = (cache.state[restoreKey] ||=\n {} as ScrollRestorationByElement)\n\n for (const [target, position] of trackedScrollEntries) {\n let selector: string | undefined\n\n if (target === windowScrollTarget) {\n selector = windowScrollTarget\n } else if (target.isConnected) {\n const attrId = target.getAttribute(scrollRestorationIdAttribute)\n selector = attrId\n ? `[${scrollRestorationIdAttribute}=\"${attrId}\"]`\n : getCssSelector(target)\n }\n\n if (!selector) {\n continue\n }\n\n keyEntry[selector] = position\n }\n }\n\n document.addEventListener('scroll', onScroll, true)\n router.subscribe('onBeforeLoad', (event) => {\n snapshotCurrentScrollTargets(\n event.fromLocation ? getKey(event.fromLocation) : undefined,\n )\n trackedScrollEntries.clear()\n })\n window.addEventListener('pagehide', () => {\n snapshotCurrentScrollTargets(\n getKey(\n router.stores.resolvedLocation.get() ?? router.stores.location.get(),\n ),\n )\n cache.persist()\n })\n\n // Restore destination scroll after the new route has rendered.\n router.subscribe('onRendered', (event) => {\n const cacheKey = getKey(event.toLocation)\n const behavior = router.options.scrollRestorationBehavior\n const scrollToTopSelectors = router.options.scrollToTopSelectors\n trackedScrollEntries.clear()\n\n if (!router.resetNextScroll) {\n router.resetNextScroll = true\n return\n }\n\n if (\n typeof router.options.scrollRestoration === 'function' &&\n !router.options.scrollRestoration({ location: router.latestLocation })\n ) {\n return\n }\n\n ignoreScroll = true\n\n try {\n const elementEntries = router.isScrollRestoring\n ? cache.state[cacheKey]\n : undefined\n let restored = false\n\n if (elementEntries) {\n for (const elementSelector in elementEntries) {\n const entry = elementEntries[elementSelector]\n\n if (!isPlainObject(entry)) {\n continue\n }\n\n const { scrollX, scrollY } = entry as {\n scrollX?: unknown\n scrollY?: unknown\n }\n\n if (!Number.isFinite(scrollX) || !Number.isFinite(scrollY)) {\n continue\n }\n\n if (elementSelector === windowScrollTarget) {\n window.scrollTo({\n top: scrollY as number,\n left: scrollX as number,\n behavior,\n })\n restored = 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 as number\n element.scrollTop = scrollY as number\n restored = true\n }\n }\n }\n }\n\n if (!restored) {\n const hash = router.history.location.hash.slice(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 } else {\n const scrollOptions = {\n top: 0,\n left: 0,\n behavior,\n }\n\n window.scrollTo(scrollOptions)\n if (scrollToTopSelectors) {\n for (const selector of scrollToTopSelectors) {\n if (selector === windowScrollTarget) continue\n const element =\n typeof selector === 'function'\n ? selector()\n : document.querySelector(selector)\n if (element) {\n element.scrollTo(scrollOptions)\n }\n }\n }\n }\n }\n } finally {\n ignoreScroll = false\n }\n\n if (router.isScrollRestoring) {\n cache.set((state) => {\n state[cacheKey] ||= {} as ScrollRestorationByElement\n return state\n })\n }\n })\n}\n"],"mappings":";;;AAuBA,SAAS,wBAAwB;AAC/B,KAAI;AACF,SAAO,OAAO,WAAW,eACvB,OAAO,OAAO,mBAAmB,WAC/B,OAAO,iBACP,KAAA;SACE;AAEN;;;AAKJ,MAAa,aAAa;AAE1B,SAAS,+BAA8D;CACrE,MAAM,qBAAqB,uBAAuB;AAClD,KAAI,CAAC,mBACH,QAAO;CAGT,IAAI,QAAgC,EAAE;AAEtC,KAAI;EACF,MAAM,SAAS,KAAK,MAAM,mBAAmB,QAAA,8BAAmB,IAAI,KAAK;AACzE,MAAI,cAAc,OAAO,CACvB,SAAQ;SAEJ;CAIR,MAAM,gBAAgB;AACpB,MAAI;AACF,sBAAmB,QAAQ,YAAY,KAAK,UAAU,MAAM,CAAC;UACvD;AACN,OAAA,QAAA,IAAA,aAA6B,aAC3B,SAAQ,KACN,4EACD;;;AAKP,QAAO;EACL,IAAI,QAAQ;AACV,UAAO;;EAET,MAAM,YAAY;AAChB,WAAQ,iBAAiB,SAAS,MAAM,IAAI;;EAE9C;EACD;;AAGH,MAAa,yBAAyB,8BAA8B;;;;;;;AAQpE,MAAa,kCAAkC,aAA6B;AAC1E,QAAO,SAAS,MAAM,aAAc,SAAS;;AAG/C,SAAS,eAAe,IAAiB;CACvC,MAAM,OAAO,EAAE;CACf,IAAI;AACJ,QAAQ,SAAS,GAAG,YAAa;AAC/B,OAAK,KACH,GAAG,GAAG,QAAQ,aAAa,MAAM,UAAU,QAAQ,KAAK,OAAO,UAAU,GAAG,GAAG,EAAE,GAClF;AACD,OAAK;;AAEP,QAAO,GAAG,KAAK,SAAS,CAAC,KAAK,MAAM,GAAG,aAAa;;AAGtD,SAAgB,iCACd,QACA,SAYoC;CAEpC,MAAM,cADS,QAAQ,UAAU,gCACP,OAAO,eAAe;AAEhD,KAAI,QAAQ,GACV,QAAO,wBAAwB,MAAM,cACnC,IAAI,6BAA6B,IAAI,QAAQ,GAAG;CAIpD,MAAM,UAAU,QAAQ,cAAc;AACtC,KAAI,CAAC,QACH;AAGF,QAAO,wBAAwB,MAAM,cACnC,mBAAmB,SAAS,qBAAqB,eAAe,QAAQ;;AAI5E,IAAI,eAAe;AACnB,MAAM,qBAAqB;AAC3B,MAAM,+BAA+B;AAGrC,SAAgB,uBAAuB,QAAmB,OAAiB;AACzE,KAAI,CAAC,0BAA0B,EAAE,YAAY,OAAO,UAClD;CAGF,MAAM,QAAQ;AAKd,KAFE,SAAS,OAAO,QAAQ,qBAAqB,MAG7C,QAAO,oBAAoB;AAG7B,MACG,YAAY,OAAO,aACpB,OAAO,4BACP,CAAC,MAED;AAGF,QAAO,2BAA2B;AAClC,gBAAe;CAEf,MAAM,SACJ,OAAO,QAAQ,2BAA2B;CAC5C,MAAM,uCAAuB,IAAI,KAA2C;AAE5E,QAAO,QAAQ,oBAAoB;CAEnC,MAAM,YAAY,UAAiB;AACjC,MAAI,gBAAgB,CAAC,OAAO,kBAC1B;AAGF,MAAI,MAAM,WAAW,YAAY,MAAM,WAAW,OAChD,sBAAqB,IAAI,oBAAoB;GAC3C,SAAS,OAAO,WAAW;GAC3B,SAAS,OAAO,WAAW;GAC5B,CAAC;OACG;GACL,MAAM,SAAS,MAAM;AACrB,wBAAqB,IAAI,QAAQ;IAC/B,SAAS,OAAO,cAAc;IAC9B,SAAS,OAAO,aAAa;IAC9B,CAAC;;;CAKN,MAAM,gCAAgC,eAAwB;AAC5D,MACE,CAAC,OAAO,qBACR,CAAC,cACD,qBAAqB,SAAS,KAC9B,CAAC,MAED;EAGF,MAAM,WAAY,MAAM,MAAM,gBAC5B,EAAE;AAEJ,OAAK,MAAM,CAAC,QAAQ,aAAa,sBAAsB;GACrD,IAAI;AAEJ,OAAI,WAAW,mBACb,YAAW;YACF,OAAO,aAAa;IAC7B,MAAM,SAAS,OAAO,aAAa,6BAA6B;AAChE,eAAW,SACP,IAAI,6BAA6B,IAAI,OAAO,MAC5C,eAAe,OAAO;;AAG5B,OAAI,CAAC,SACH;AAGF,YAAS,YAAY;;;AAIzB,UAAS,iBAAiB,UAAU,UAAU,KAAK;AACnD,QAAO,UAAU,iBAAiB,UAAU;AAC1C,+BACE,MAAM,eAAe,OAAO,MAAM,aAAa,GAAG,KAAA,EACnD;AACD,uBAAqB,OAAO;GAC5B;AACF,QAAO,iBAAiB,kBAAkB;AACxC,+BACE,OACE,OAAO,OAAO,iBAAiB,KAAK,IAAI,OAAO,OAAO,SAAS,KAAK,CACrE,CACF;AACD,QAAM,SAAS;GACf;AAGF,QAAO,UAAU,eAAe,UAAU;EACxC,MAAM,WAAW,OAAO,MAAM,WAAW;EACzC,MAAM,WAAW,OAAO,QAAQ;EAChC,MAAM,uBAAuB,OAAO,QAAQ;AAC5C,uBAAqB,OAAO;AAE5B,MAAI,CAAC,OAAO,iBAAiB;AAC3B,UAAO,kBAAkB;AACzB;;AAGF,MACE,OAAO,OAAO,QAAQ,sBAAsB,cAC5C,CAAC,OAAO,QAAQ,kBAAkB,EAAE,UAAU,OAAO,gBAAgB,CAAC,CAEtE;AAGF,iBAAe;AAEf,MAAI;GACF,MAAM,iBAAiB,OAAO,oBAC1B,MAAM,MAAM,YACZ,KAAA;GACJ,IAAI,WAAW;AAEf,OAAI,eACF,MAAK,MAAM,mBAAmB,gBAAgB;IAC5C,MAAM,QAAQ,eAAe;AAE7B,QAAI,CAAC,cAAc,MAAM,CACvB;IAGF,MAAM,EAAE,SAAS,YAAY;AAK7B,QAAI,CAAC,OAAO,SAAS,QAAQ,IAAI,CAAC,OAAO,SAAS,QAAQ,CACxD;AAGF,QAAI,oBAAoB,oBAAoB;AAC1C,YAAO,SAAS;MACd,KAAK;MACL,MAAM;MACN;MACD,CAAC;AACF,gBAAW;eACF,iBAAiB;KAC1B,IAAI;AAEJ,SAAI;AACF,gBAAU,SAAS,cAAc,gBAAgB;aAC3C;AACN;;AAGF,SAAI,SAAS;AACX,cAAQ,aAAa;AACrB,cAAQ,YAAY;AACpB,iBAAW;;;;AAMnB,OAAI,CAAC,UAAU;IACb,MAAM,OAAO,OAAO,QAAQ,SAAS,KAAK,MAAM,EAAE;AAElD,QAAI,MAAM;KACR,MAAM,4BACJ,OAAO,QAAQ,OAAO,+BAA+B;AAEvD,SAAI,2BAA2B;MAC7B,MAAM,KAAK,SAAS,eAAe,KAAK;AACxC,UAAI,GACF,IAAG,eAAe,0BAA0B;;WAG3C;KACL,MAAM,gBAAgB;MACpB,KAAK;MACL,MAAM;MACN;MACD;AAED,YAAO,SAAS,cAAc;AAC9B,SAAI,qBACF,MAAK,MAAM,YAAY,sBAAsB;AAC3C,UAAI,aAAa,mBAAoB;MACrC,MAAM,UACJ,OAAO,aAAa,aAChB,UAAU,GACV,SAAS,cAAc,SAAS;AACtC,UAAI,QACF,SAAQ,SAAS,cAAc;;;;YAMjC;AACR,kBAAe;;AAGjB,MAAI,OAAO,kBACT,OAAM,KAAK,UAAU;AACnB,SAAM,cAAc,EAAE;AACtB,UAAO;IACP;GAEJ"}
@@ -1,9 +1,9 @@
1
1
  import { decode, encode } from "./qss.js";
2
2
  //#region src/searchParams.ts
3
3
  /** Default `parseSearch` that strips leading '?' and JSON-parses values. */
4
- var defaultParseSearch = parseSearchWith(JSON.parse);
4
+ const defaultParseSearch = parseSearchWith(JSON.parse);
5
5
  /** Default `stringifySearch` using JSON.stringify for complex values. */
6
- var defaultStringifySearch = stringifySearchWith(JSON.stringify, JSON.parse);
6
+ const defaultStringifySearch = stringifySearchWith(JSON.stringify, JSON.parse);
7
7
  /**
8
8
  * Build a `parseSearch` function using a provided JSON-like parser.
9
9
  *
@@ -1 +1 @@
1
- {"version":3,"file":"searchParams.js","names":[],"sources":["../../src/searchParams.ts"],"sourcesContent":["import { decode, encode } from './qss'\nimport type { AnySchema } from './validators'\n\n/** Default `parseSearch` that strips leading '?' and JSON-parses values. */\nexport const defaultParseSearch = parseSearchWith(JSON.parse)\n/** Default `stringifySearch` using JSON.stringify for complex values. */\nexport const defaultStringifySearch = stringifySearchWith(\n JSON.stringify,\n JSON.parse,\n)\n\n/**\n * Build a `parseSearch` function using a provided JSON-like parser.\n *\n * The returned function strips a leading `?`, decodes values, and attempts to\n * JSON-parse string values using the given `parser`.\n *\n * @param parser Function to parse a string value (e.g. `JSON.parse`).\n * @returns A `parseSearch` function compatible with `Router` options.\n * @link https://tanstack.com/router/latest/docs/framework/react/guide/custom-search-param-serialization\n */\nexport function parseSearchWith(parser: (str: string) => any) {\n return (searchStr: string): AnySchema => {\n if (searchStr[0] === '?') {\n searchStr = searchStr.substring(1)\n }\n\n const query: Record<string, unknown> = decode(searchStr)\n\n // Try to parse any query params that might be json\n for (const key in query) {\n const value = query[key]\n if (typeof value === 'string') {\n try {\n query[key] = parser(value)\n } catch (_err) {\n // silent\n }\n }\n }\n\n return query\n }\n}\n\n/**\n * Build a `stringifySearch` function using a provided serializer.\n *\n * Non-primitive values are serialized with `stringify`. If a `parser` is\n * supplied, string values that are parseable are re-serialized to ensure\n * symmetry with `parseSearch`.\n *\n * @param stringify Function to serialize a value (e.g. `JSON.stringify`).\n * @param parser Optional parser to detect parseable strings.\n * @returns A `stringifySearch` function compatible with `Router` options.\n * @link https://tanstack.com/router/latest/docs/framework/react/guide/custom-search-param-serialization\n */\nexport function stringifySearchWith(\n stringify: (search: any) => string,\n parser?: (str: string) => any,\n) {\n const hasParser = typeof parser === 'function'\n function stringifyValue(val: any) {\n if (typeof val === 'object' && val !== null) {\n try {\n return stringify(val)\n } catch (_err) {\n // silent\n }\n } else if (hasParser && typeof val === 'string') {\n try {\n // Check if it's a valid parseable string.\n // If it is, then stringify it again.\n parser(val)\n return stringify(val)\n } catch (_err) {\n // silent\n }\n }\n return val\n }\n\n return (search: Record<string, any>) => {\n const searchStr = encode(search, stringifyValue)\n return searchStr ? `?${searchStr}` : ''\n }\n}\n\nexport type SearchSerializer = (searchObj: Record<string, any>) => string\nexport type SearchParser = (searchStr: string) => Record<string, any>\n"],"mappings":";;;AAIA,IAAa,qBAAqB,gBAAgB,KAAK,MAAM;;AAE7D,IAAa,yBAAyB,oBACpC,KAAK,WACL,KAAK,MACN;;;;;;;;;;;AAYD,SAAgB,gBAAgB,QAA8B;AAC5D,SAAQ,cAAiC;AACvC,MAAI,UAAU,OAAO,IACnB,aAAY,UAAU,UAAU,EAAE;EAGpC,MAAM,QAAiC,OAAO,UAAU;AAGxD,OAAK,MAAM,OAAO,OAAO;GACvB,MAAM,QAAQ,MAAM;AACpB,OAAI,OAAO,UAAU,SACnB,KAAI;AACF,UAAM,OAAO,OAAO,MAAM;YACnB,MAAM;;AAMnB,SAAO;;;;;;;;;;;;;;;AAgBX,SAAgB,oBACd,WACA,QACA;CACA,MAAM,YAAY,OAAO,WAAW;CACpC,SAAS,eAAe,KAAU;AAChC,MAAI,OAAO,QAAQ,YAAY,QAAQ,KACrC,KAAI;AACF,UAAO,UAAU,IAAI;WACd,MAAM;WAGN,aAAa,OAAO,QAAQ,SACrC,KAAI;AAGF,UAAO,IAAI;AACX,UAAO,UAAU,IAAI;WACd,MAAM;AAIjB,SAAO;;AAGT,SAAQ,WAAgC;EACtC,MAAM,YAAY,OAAO,QAAQ,eAAe;AAChD,SAAO,YAAY,IAAI,cAAc"}
1
+ {"version":3,"file":"searchParams.js","names":[],"sources":["../../src/searchParams.ts"],"sourcesContent":["import { decode, encode } from './qss'\nimport type { AnySchema } from './validators'\n\n/** Default `parseSearch` that strips leading '?' and JSON-parses values. */\nexport const defaultParseSearch = parseSearchWith(JSON.parse)\n/** Default `stringifySearch` using JSON.stringify for complex values. */\nexport const defaultStringifySearch = stringifySearchWith(\n JSON.stringify,\n JSON.parse,\n)\n\n/**\n * Build a `parseSearch` function using a provided JSON-like parser.\n *\n * The returned function strips a leading `?`, decodes values, and attempts to\n * JSON-parse string values using the given `parser`.\n *\n * @param parser Function to parse a string value (e.g. `JSON.parse`).\n * @returns A `parseSearch` function compatible with `Router` options.\n * @link https://tanstack.com/router/latest/docs/framework/react/guide/custom-search-param-serialization\n */\nexport function parseSearchWith(parser: (str: string) => any) {\n return (searchStr: string): AnySchema => {\n if (searchStr[0] === '?') {\n searchStr = searchStr.substring(1)\n }\n\n const query: Record<string, unknown> = decode(searchStr)\n\n // Try to parse any query params that might be json\n for (const key in query) {\n const value = query[key]\n if (typeof value === 'string') {\n try {\n query[key] = parser(value)\n } catch (_err) {\n // silent\n }\n }\n }\n\n return query\n }\n}\n\n/**\n * Build a `stringifySearch` function using a provided serializer.\n *\n * Non-primitive values are serialized with `stringify`. If a `parser` is\n * supplied, string values that are parseable are re-serialized to ensure\n * symmetry with `parseSearch`.\n *\n * @param stringify Function to serialize a value (e.g. `JSON.stringify`).\n * @param parser Optional parser to detect parseable strings.\n * @returns A `stringifySearch` function compatible with `Router` options.\n * @link https://tanstack.com/router/latest/docs/framework/react/guide/custom-search-param-serialization\n */\nexport function stringifySearchWith(\n stringify: (search: any) => string,\n parser?: (str: string) => any,\n) {\n const hasParser = typeof parser === 'function'\n function stringifyValue(val: any) {\n if (typeof val === 'object' && val !== null) {\n try {\n return stringify(val)\n } catch (_err) {\n // silent\n }\n } else if (hasParser && typeof val === 'string') {\n try {\n // Check if it's a valid parseable string.\n // If it is, then stringify it again.\n parser(val)\n return stringify(val)\n } catch (_err) {\n // silent\n }\n }\n return val\n }\n\n return (search: Record<string, any>) => {\n const searchStr = encode(search, stringifyValue)\n return searchStr ? `?${searchStr}` : ''\n }\n}\n\nexport type SearchSerializer = (searchObj: Record<string, any>) => string\nexport type SearchParser = (searchStr: string) => Record<string, any>\n"],"mappings":";;;AAIA,MAAa,qBAAqB,gBAAgB,KAAK,MAAM;;AAE7D,MAAa,yBAAyB,oBACpC,KAAK,WACL,KAAK,MACN;;;;;;;;;;;AAYD,SAAgB,gBAAgB,QAA8B;AAC5D,SAAQ,cAAiC;AACvC,MAAI,UAAU,OAAO,IACnB,aAAY,UAAU,UAAU,EAAE;EAGpC,MAAM,QAAiC,OAAO,UAAU;AAGxD,OAAK,MAAM,OAAO,OAAO;GACvB,MAAM,QAAQ,MAAM;AACpB,OAAI,OAAO,UAAU,SACnB,KAAI;AACF,UAAM,OAAO,OAAO,MAAM;YACnB,MAAM;;AAMnB,SAAO;;;;;;;;;;;;;;;AAgBX,SAAgB,oBACd,WACA,QACA;CACA,MAAM,YAAY,OAAO,WAAW;CACpC,SAAS,eAAe,KAAU;AAChC,MAAI,OAAO,QAAQ,YAAY,QAAQ,KACrC,KAAI;AACF,UAAO,UAAU,IAAI;WACd,MAAM;WAGN,aAAa,OAAO,QAAQ,SACrC,KAAI;AAGF,UAAO,IAAI;AACX,UAAO,UAAU,IAAI;WACd,MAAM;AAIjB,SAAO;;AAGT,SAAQ,WAAgC;EACtC,MAAM,YAAY,OAAO,QAAQ,eAAe;AAChD,SAAO,YAAY,IAAI,cAAc"}
@@ -1,6 +1,6 @@
1
1
  //#region src/ssr/constants.ts
2
- var GLOBAL_TSR = "$_TSR";
3
- var TSR_SCRIPT_BARRIER_ID = "$tsr-stream-barrier";
2
+ const GLOBAL_TSR = "$_TSR";
3
+ const TSR_SCRIPT_BARRIER_ID = "$tsr-stream-barrier";
4
4
  //#endregion
5
5
  export { GLOBAL_TSR, TSR_SCRIPT_BARRIER_ID };
6
6
 
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","names":[],"sources":["../../../src/ssr/constants.ts"],"sourcesContent":["export const GLOBAL_TSR = '$_TSR'\nexport declare const GLOBAL_SEROVAL: '$R'\nexport const TSR_SCRIPT_BARRIER_ID = '$tsr-stream-barrier'\n"],"mappings":";AAAA,IAAa,aAAa;AAE1B,IAAa,wBAAwB"}
1
+ {"version":3,"file":"constants.js","names":[],"sources":["../../../src/ssr/constants.ts"],"sourcesContent":["export const GLOBAL_TSR = '$_TSR'\nexport declare const GLOBAL_SEROVAL: '$R'\nexport const TSR_SCRIPT_BARRIER_ID = '$tsr-stream-barrier'\n"],"mappings":";AAAA,MAAa,aAAa;AAE1B,MAAa,wBAAwB"}
@@ -19,8 +19,8 @@ var RawStream = class {
19
19
  this.hint = options?.hint ?? "binary";
20
20
  }
21
21
  };
22
- var BufferCtor = globalThis.Buffer;
23
- var hasNodeBuffer = !!BufferCtor && typeof BufferCtor.from === "function";
22
+ const BufferCtor = globalThis.Buffer;
23
+ const hasNodeBuffer = !!BufferCtor && typeof BufferCtor.from === "function";
24
24
  function uint8ArrayToBase64(bytes) {
25
25
  if (bytes.length === 0) return "";
26
26
  if (hasNodeBuffer) return BufferCtor.from(bytes).toString("base64");
@@ -43,9 +43,9 @@ function base64ToUint8Array(base64) {
43
43
  for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
44
44
  return bytes;
45
45
  }
46
- var RAW_STREAM_FACTORY_BINARY = Object.create(null);
47
- var RAW_STREAM_FACTORY_TEXT = Object.create(null);
48
- var RAW_STREAM_FACTORY_CONSTRUCTOR_BINARY = (stream) => new ReadableStream({ start(controller) {
46
+ const RAW_STREAM_FACTORY_BINARY = Object.create(null);
47
+ const RAW_STREAM_FACTORY_TEXT = Object.create(null);
48
+ const RAW_STREAM_FACTORY_CONSTRUCTOR_BINARY = (stream) => new ReadableStream({ start(controller) {
49
49
  stream.on({
50
50
  next(base64) {
51
51
  try {
@@ -62,8 +62,8 @@ var RAW_STREAM_FACTORY_CONSTRUCTOR_BINARY = (stream) => new ReadableStream({ sta
62
62
  }
63
63
  });
64
64
  } });
65
- var textEncoderForFactory = new TextEncoder();
66
- var RAW_STREAM_FACTORY_CONSTRUCTOR_TEXT = (stream) => {
65
+ const textEncoderForFactory = new TextEncoder();
66
+ const RAW_STREAM_FACTORY_CONSTRUCTOR_TEXT = (stream) => {
67
67
  return new ReadableStream({ start(controller) {
68
68
  stream.on({
69
69
  next(value) {
@@ -83,8 +83,8 @@ var RAW_STREAM_FACTORY_CONSTRUCTOR_TEXT = (stream) => {
83
83
  });
84
84
  } });
85
85
  };
86
- var FACTORY_BINARY = `(s=>new ReadableStream({start(c){s.on({next(b){try{const d=atob(b),a=new Uint8Array(d.length);for(let i=0;i<d.length;i++)a[i]=d.charCodeAt(i);c.enqueue(a)}catch(_){}},throw(e){c.error(e)},return(){try{c.close()}catch(_){}}})}}))`;
87
- var FACTORY_TEXT = `(s=>{const e=new TextEncoder();return new ReadableStream({start(c){s.on({next(v){try{if(typeof v==='string'){c.enqueue(e.encode(v))}else{const d=atob(v.$b64),a=new Uint8Array(d.length);for(let i=0;i<d.length;i++)a[i]=d.charCodeAt(i);c.enqueue(a)}}catch(_){}},throw(x){c.error(x)},return(){try{c.close()}catch(_){}}})}})})`;
86
+ const FACTORY_BINARY = `(s=>new ReadableStream({start(c){s.on({next(b){try{const d=atob(b),a=new Uint8Array(d.length);for(let i=0;i<d.length;i++)a[i]=d.charCodeAt(i);c.enqueue(a)}catch(_){}},throw(e){c.error(e)},return(){try{c.close()}catch(_){}}})}}))`;
87
+ const FACTORY_TEXT = `(s=>{const e=new TextEncoder();return new ReadableStream({start(c){s.on({next(v){try{if(typeof v==='string'){c.enqueue(e.encode(v))}else{const d=atob(v.$b64),a=new Uint8Array(d.length);for(let i=0;i<d.length;i++)a[i]=d.charCodeAt(i);c.enqueue(a)}}catch(_){}},throw(x){c.error(x)},return(){try{c.close()}catch(_){}}})}})})`;
88
88
  function toBinaryStream(readable) {
89
89
  const stream = createStream();
90
90
  const reader = readable.getReader();
@@ -145,7 +145,7 @@ function toTextStream(readable) {
145
145
  * - 'binary': Always base64 encode (default)
146
146
  * - 'text': Try UTF-8 first, fallback to base64 for invalid UTF-8
147
147
  */
148
- var RawStreamSSRPlugin = /* @__PURE__ */ createPlugin({
148
+ const RawStreamSSRPlugin = /* @__PURE__ */ createPlugin({
149
149
  tag: "tss/RawStream",
150
150
  extends: [/* @__PURE__ */ createPlugin({
151
151
  tag: "tss/RawStreamFactory",
@@ -1 +1 @@
1
- {"version":3,"file":"RawStream.js","names":[],"sources":["../../../../src/ssr/serializer/RawStream.ts"],"sourcesContent":["import { createPlugin, createStream } from 'seroval'\nimport type { PluginData, PluginInfo, SerovalNode } from 'seroval'\n\n/**\n * Hint for RawStream encoding strategy during SSR serialization.\n * - 'binary': Always use base64 encoding (best for binary data like files, images)\n * - 'text': Try UTF-8 first, fallback to base64 (best for text-heavy data like RSC payloads)\n */\nexport type RawStreamHint = 'binary' | 'text'\n\n/**\n * Options for RawStream configuration.\n */\nexport interface RawStreamOptions {\n /**\n * Encoding hint for SSR serialization.\n * - 'binary' (default): Always use base64 encoding\n * - 'text': Try UTF-8 first, fallback to base64 for invalid UTF-8 chunks\n */\n hint?: RawStreamHint\n}\n\n/**\n * Marker class for ReadableStream<Uint8Array> that should be serialized\n * with base64 encoding (SSR) or binary framing (server functions).\n *\n * Wrap your binary streams with this to get efficient serialization:\n * ```ts\n * // For binary data (files, images, etc.)\n * return { data: new RawStream(file.stream()) }\n *\n * // For text-heavy data (RSC payloads, etc.)\n * return { data: new RawStream(rscStream, { hint: 'text' }) }\n * ```\n */\nexport class RawStream {\n public readonly hint: RawStreamHint\n\n constructor(\n public readonly stream: ReadableStream<Uint8Array>,\n options?: RawStreamOptions,\n ) {\n this.hint = options?.hint ?? 'binary'\n }\n}\n\n/**\n * Callback type for RPC plugin to register raw streams with multiplexer\n */\nexport type OnRawStreamCallback = (\n streamId: number,\n stream: ReadableStream<Uint8Array>,\n) => void\n\n// Base64 helpers used in both Node and browser.\n// In Node-like runtimes, prefer Buffer for speed and compatibility.\nconst BufferCtor: any = (globalThis as any).Buffer\nconst hasNodeBuffer = !!BufferCtor && typeof BufferCtor.from === 'function'\n\nfunction uint8ArrayToBase64(bytes: Uint8Array): string {\n if (bytes.length === 0) return ''\n\n if (hasNodeBuffer) {\n return BufferCtor.from(bytes).toString('base64')\n }\n\n // Browser fallback: chunked String.fromCharCode + btoa\n const CHUNK_SIZE = 0x8000 // 32KB chunks to avoid stack overflow\n const chunks: Array<string> = []\n for (let i = 0; i < bytes.length; i += CHUNK_SIZE) {\n const chunk = bytes.subarray(i, i + CHUNK_SIZE)\n chunks.push(String.fromCharCode.apply(null, chunk as any))\n }\n return btoa(chunks.join(''))\n}\n\nfunction base64ToUint8Array(base64: string): Uint8Array {\n if (base64.length === 0) return new Uint8Array(0)\n\n if (hasNodeBuffer) {\n const buf = BufferCtor.from(base64, 'base64')\n return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength)\n }\n\n const binary = atob(base64)\n const bytes = new Uint8Array(binary.length)\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i)\n }\n return bytes\n}\n\n// Factory sentinels - use null-proto objects to avoid prototype surprises\nconst RAW_STREAM_FACTORY_BINARY: Record<string, never> = Object.create(null)\nconst RAW_STREAM_FACTORY_TEXT: Record<string, never> = Object.create(null)\n\n// Factory constructor for binary mode - converts seroval stream to ReadableStream<Uint8Array>\n// All chunks are base64 encoded strings\nconst RAW_STREAM_FACTORY_CONSTRUCTOR_BINARY = (\n stream: ReturnType<typeof createStream>,\n) =>\n new ReadableStream<Uint8Array>({\n start(controller) {\n stream.on({\n next(base64: string) {\n try {\n controller.enqueue(base64ToUint8Array(base64))\n } catch {\n // Stream may be closed\n }\n },\n throw(error: unknown) {\n controller.error(error)\n },\n return() {\n try {\n controller.close()\n } catch {\n // Stream may already be closed\n }\n },\n })\n },\n })\n\n// Factory constructor for text mode - converts seroval stream to ReadableStream<Uint8Array>\n// Chunks are either strings (UTF-8) or { $b64: string } (base64 fallback)\n// Use module-level TextEncoder to avoid per-factory allocation\nconst textEncoderForFactory = new TextEncoder()\nconst RAW_STREAM_FACTORY_CONSTRUCTOR_TEXT = (\n stream: ReturnType<typeof createStream>,\n) => {\n return new ReadableStream<Uint8Array>({\n start(controller) {\n stream.on({\n next(value: string | { $b64: string }) {\n try {\n if (typeof value === 'string') {\n controller.enqueue(textEncoderForFactory.encode(value))\n } else {\n controller.enqueue(base64ToUint8Array(value.$b64))\n }\n } catch {\n // Stream may be closed\n }\n },\n throw(error: unknown) {\n controller.error(error)\n },\n return() {\n try {\n controller.close()\n } catch {\n // Stream may already be closed\n }\n },\n })\n },\n })\n}\n\n// Minified factory function for binary mode - all chunks are base64 strings\n// This must be self-contained since it's injected into the HTML\nconst FACTORY_BINARY = `(s=>new ReadableStream({start(c){s.on({next(b){try{const d=atob(b),a=new Uint8Array(d.length);for(let i=0;i<d.length;i++)a[i]=d.charCodeAt(i);c.enqueue(a)}catch(_){}},throw(e){c.error(e)},return(){try{c.close()}catch(_){}}})}}))`\n\n// Minified factory function for text mode - chunks are string or {$b64: string}\n// Uses cached TextEncoder for performance\nconst FACTORY_TEXT = `(s=>{const e=new TextEncoder();return new ReadableStream({start(c){s.on({next(v){try{if(typeof v==='string'){c.enqueue(e.encode(v))}else{const d=atob(v.$b64),a=new Uint8Array(d.length);for(let i=0;i<d.length;i++)a[i]=d.charCodeAt(i);c.enqueue(a)}}catch(_){}},throw(x){c.error(x)},return(){try{c.close()}catch(_){}}})}})})`\n\n// Convert ReadableStream<Uint8Array> to seroval stream with base64-encoded chunks (binary mode)\nfunction toBinaryStream(readable: ReadableStream<Uint8Array>) {\n const stream = createStream()\n const reader = readable.getReader()\n\n // Use iterative loop instead of recursive async to avoid stack accumulation\n ;(async () => {\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) {\n stream.return(undefined)\n break\n }\n stream.next(uint8ArrayToBase64(value))\n }\n } catch (error) {\n stream.throw(error)\n } finally {\n reader.releaseLock()\n }\n })()\n\n return stream\n}\n\n// Convert ReadableStream<Uint8Array> to seroval stream with UTF-8 first, base64 fallback (text mode)\nfunction toTextStream(readable: ReadableStream<Uint8Array>) {\n const stream = createStream()\n const reader = readable.getReader()\n const decoder = new TextDecoder('utf-8', { fatal: true })\n\n // Use iterative loop instead of recursive async to avoid stack accumulation\n ;(async () => {\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) {\n // Flush any remaining bytes in the decoder\n try {\n const remaining = decoder.decode()\n if (remaining.length > 0) {\n stream.next(remaining)\n }\n } catch {\n // Ignore decode errors on flush\n }\n stream.return(undefined)\n break\n }\n\n try {\n // Try UTF-8 decode first\n const text = decoder.decode(value, { stream: true })\n if (text.length > 0) {\n stream.next(text)\n }\n } catch {\n // UTF-8 decode failed, fallback to base64\n stream.next({ $b64: uint8ArrayToBase64(value) })\n }\n }\n } catch (error) {\n stream.throw(error)\n } finally {\n reader.releaseLock()\n }\n })()\n\n return stream\n}\n\n// Factory plugin for binary mode\nconst RawStreamFactoryBinaryPlugin = /* @__PURE__ */ createPlugin<\n Record<string, never>,\n PluginInfo\n>({\n tag: 'tss/RawStreamFactory',\n test(value) {\n return value === RAW_STREAM_FACTORY_BINARY\n },\n parse: {\n sync(_value, _ctx, _data) {\n return {}\n },\n async async(_value, _ctx, _data) {\n return {}\n },\n stream(_value, _ctx, _data) {\n return {}\n },\n },\n serialize(_node, _ctx, _data) {\n return FACTORY_BINARY\n },\n deserialize(_node, _ctx, _data) {\n return RAW_STREAM_FACTORY_BINARY\n },\n})\n\n// Factory plugin for text mode\nconst RawStreamFactoryTextPlugin = /* @__PURE__ */ createPlugin<\n Record<string, never>,\n PluginInfo\n>({\n tag: 'tss/RawStreamFactoryText',\n test(value) {\n return value === RAW_STREAM_FACTORY_TEXT\n },\n parse: {\n sync(_value, _ctx, _data) {\n return {}\n },\n async async(_value, _ctx, _data) {\n return {}\n },\n stream(_value, _ctx, _data) {\n return {}\n },\n },\n serialize(_node, _ctx, _data) {\n return FACTORY_TEXT\n },\n deserialize(_node, _ctx, _data) {\n return RAW_STREAM_FACTORY_TEXT\n },\n})\n\nexport interface RawStreamSSRNode extends PluginInfo {\n hint: SerovalNode\n factory: SerovalNode\n stream: SerovalNode\n}\n\nexport interface RawStreamRPCNode extends PluginInfo {\n streamId: SerovalNode\n}\n\n/**\n * SSR Plugin - uses base64 or UTF-8+base64 encoding for chunks, delegates to seroval's stream mechanism.\n * Used during SSR when serializing to JavaScript code for HTML injection.\n *\n * Supports two modes based on RawStream hint:\n * - 'binary': Always base64 encode (default)\n * - 'text': Try UTF-8 first, fallback to base64 for invalid UTF-8\n */\nexport const RawStreamSSRPlugin = /* @__PURE__ */ createPlugin<\n RawStream,\n RawStreamSSRNode\n>({\n tag: 'tss/RawStream',\n extends: [RawStreamFactoryBinaryPlugin, RawStreamFactoryTextPlugin],\n\n test(value: unknown) {\n return value instanceof RawStream\n },\n\n parse: {\n sync(value: RawStream, ctx, _data) {\n // Sync parse not really supported for streams, return empty stream\n const factory =\n value.hint === 'text'\n ? RAW_STREAM_FACTORY_TEXT\n : RAW_STREAM_FACTORY_BINARY\n return {\n hint: ctx.parse(value.hint),\n factory: ctx.parse(factory),\n stream: ctx.parse(createStream()),\n }\n },\n async async(value: RawStream, ctx, _data) {\n const factory =\n value.hint === 'text'\n ? RAW_STREAM_FACTORY_TEXT\n : RAW_STREAM_FACTORY_BINARY\n const encodedStream =\n value.hint === 'text'\n ? toTextStream(value.stream)\n : toBinaryStream(value.stream)\n return {\n hint: await ctx.parse(value.hint),\n factory: await ctx.parse(factory),\n stream: await ctx.parse(encodedStream),\n }\n },\n stream(value: RawStream, ctx, _data) {\n const factory =\n value.hint === 'text'\n ? RAW_STREAM_FACTORY_TEXT\n : RAW_STREAM_FACTORY_BINARY\n const encodedStream =\n value.hint === 'text'\n ? toTextStream(value.stream)\n : toBinaryStream(value.stream)\n return {\n hint: ctx.parse(value.hint),\n factory: ctx.parse(factory),\n stream: ctx.parse(encodedStream),\n }\n },\n },\n\n serialize(node: RawStreamSSRNode, ctx, _data) {\n return (\n '(' +\n ctx.serialize(node.factory) +\n ')(' +\n ctx.serialize(node.stream) +\n ')'\n )\n },\n\n deserialize(node: RawStreamSSRNode, ctx, _data): any {\n const stream: ReturnType<typeof createStream> = ctx.deserialize(node.stream)\n const hint = ctx.deserialize(node.hint)\n return hint === 'text'\n ? RAW_STREAM_FACTORY_CONSTRUCTOR_TEXT(stream)\n : RAW_STREAM_FACTORY_CONSTRUCTOR_BINARY(stream)\n },\n})\n\n/**\n * Creates an RPC plugin instance that registers raw streams with a multiplexer.\n * Used for server function responses where we want binary framing.\n * Note: RPC always uses binary framing regardless of hint.\n *\n * @param onRawStream Callback invoked when a RawStream is encountered during serialization\n */\n/* @__NO_SIDE_EFFECTS__ */\nexport function createRawStreamRPCPlugin(onRawStream: OnRawStreamCallback) {\n // Own stream counter - sequential IDs starting at 1, independent of seroval internals\n let nextStreamId = 1\n\n return /* @__PURE__ */ createPlugin<RawStream, RawStreamRPCNode>({\n tag: 'tss/RawStream',\n\n test(value: unknown) {\n return value instanceof RawStream\n },\n\n parse: {\n async async(value: RawStream, ctx, _data: PluginData) {\n const streamId = nextStreamId++\n onRawStream(streamId, value.stream)\n return { streamId: await ctx.parse(streamId) }\n },\n stream(value: RawStream, ctx, _data: PluginData) {\n const streamId = nextStreamId++\n onRawStream(streamId, value.stream)\n return { streamId: ctx.parse(streamId) }\n },\n },\n\n serialize(): never {\n // RPC uses toCrossJSONStream which produces JSON nodes, not JS code.\n // This method is only called by crossSerialize* which we don't use.\n throw new Error(\n 'RawStreamRPCPlugin.serialize should not be called. RPC uses JSON serialization, not JS code generation.',\n )\n },\n\n deserialize(): never {\n // Client uses createRawStreamDeserializePlugin instead\n throw new Error(\n 'RawStreamRPCPlugin.deserialize should not be called. Use createRawStreamDeserializePlugin on client.',\n )\n },\n })\n}\n\n/**\n * Creates a deserialize-only plugin for client-side stream reconstruction.\n * Used in serverFnFetcher to wire up streams from frame decoder.\n *\n * @param getOrCreateStream Function to get/create a stream by ID from frame decoder\n */\nexport function createRawStreamDeserializePlugin(\n getOrCreateStream: (id: number) => ReadableStream<Uint8Array>,\n) {\n return /* @__PURE__ */ createPlugin<any, RawStreamRPCNode>({\n tag: 'tss/RawStream',\n\n test: () => false, // Client never serializes RawStream\n\n parse: {}, // Client only deserializes, never parses\n\n serialize(): never {\n // Client never serializes RawStream back to server\n throw new Error(\n 'RawStreamDeserializePlugin.serialize should not be called. Client only deserializes.',\n )\n },\n\n deserialize(node, ctx, _data) {\n // In normal seroval usage, ctx.deserialize exists.\n // Some unit tests call plugin.deserialize directly with a minimal ctx.\n const id =\n typeof (ctx as any)?.deserialize === 'function'\n ? (ctx as any).deserialize(node.streamId)\n : (node as any).streamId\n return getOrCreateStream(id as number)\n },\n })\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"RawStream.js","names":[],"sources":["../../../../src/ssr/serializer/RawStream.ts"],"sourcesContent":["import { createPlugin, createStream } from 'seroval'\nimport type { PluginData, PluginInfo, SerovalNode } from 'seroval'\n\n/**\n * Hint for RawStream encoding strategy during SSR serialization.\n * - 'binary': Always use base64 encoding (best for binary data like files, images)\n * - 'text': Try UTF-8 first, fallback to base64 (best for text-heavy data like RSC payloads)\n */\nexport type RawStreamHint = 'binary' | 'text'\n\n/**\n * Options for RawStream configuration.\n */\nexport interface RawStreamOptions {\n /**\n * Encoding hint for SSR serialization.\n * - 'binary' (default): Always use base64 encoding\n * - 'text': Try UTF-8 first, fallback to base64 for invalid UTF-8 chunks\n */\n hint?: RawStreamHint\n}\n\n/**\n * Marker class for ReadableStream<Uint8Array> that should be serialized\n * with base64 encoding (SSR) or binary framing (server functions).\n *\n * Wrap your binary streams with this to get efficient serialization:\n * ```ts\n * // For binary data (files, images, etc.)\n * return { data: new RawStream(file.stream()) }\n *\n * // For text-heavy data (RSC payloads, etc.)\n * return { data: new RawStream(rscStream, { hint: 'text' }) }\n * ```\n */\nexport class RawStream {\n public readonly hint: RawStreamHint\n\n constructor(\n public readonly stream: ReadableStream<Uint8Array>,\n options?: RawStreamOptions,\n ) {\n this.hint = options?.hint ?? 'binary'\n }\n}\n\n/**\n * Callback type for RPC plugin to register raw streams with multiplexer\n */\nexport type OnRawStreamCallback = (\n streamId: number,\n stream: ReadableStream<Uint8Array>,\n) => void\n\n// Base64 helpers used in both Node and browser.\n// In Node-like runtimes, prefer Buffer for speed and compatibility.\nconst BufferCtor: any = (globalThis as any).Buffer\nconst hasNodeBuffer = !!BufferCtor && typeof BufferCtor.from === 'function'\n\nfunction uint8ArrayToBase64(bytes: Uint8Array): string {\n if (bytes.length === 0) return ''\n\n if (hasNodeBuffer) {\n return BufferCtor.from(bytes).toString('base64')\n }\n\n // Browser fallback: chunked String.fromCharCode + btoa\n const CHUNK_SIZE = 0x8000 // 32KB chunks to avoid stack overflow\n const chunks: Array<string> = []\n for (let i = 0; i < bytes.length; i += CHUNK_SIZE) {\n const chunk = bytes.subarray(i, i + CHUNK_SIZE)\n chunks.push(String.fromCharCode.apply(null, chunk as any))\n }\n return btoa(chunks.join(''))\n}\n\nfunction base64ToUint8Array(base64: string): Uint8Array {\n if (base64.length === 0) return new Uint8Array(0)\n\n if (hasNodeBuffer) {\n const buf = BufferCtor.from(base64, 'base64')\n return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength)\n }\n\n const binary = atob(base64)\n const bytes = new Uint8Array(binary.length)\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i)\n }\n return bytes\n}\n\n// Factory sentinels - use null-proto objects to avoid prototype surprises\nconst RAW_STREAM_FACTORY_BINARY: Record<string, never> = Object.create(null)\nconst RAW_STREAM_FACTORY_TEXT: Record<string, never> = Object.create(null)\n\n// Factory constructor for binary mode - converts seroval stream to ReadableStream<Uint8Array>\n// All chunks are base64 encoded strings\nconst RAW_STREAM_FACTORY_CONSTRUCTOR_BINARY = (\n stream: ReturnType<typeof createStream>,\n) =>\n new ReadableStream<Uint8Array>({\n start(controller) {\n stream.on({\n next(base64: string) {\n try {\n controller.enqueue(base64ToUint8Array(base64))\n } catch {\n // Stream may be closed\n }\n },\n throw(error: unknown) {\n controller.error(error)\n },\n return() {\n try {\n controller.close()\n } catch {\n // Stream may already be closed\n }\n },\n })\n },\n })\n\n// Factory constructor for text mode - converts seroval stream to ReadableStream<Uint8Array>\n// Chunks are either strings (UTF-8) or { $b64: string } (base64 fallback)\n// Use module-level TextEncoder to avoid per-factory allocation\nconst textEncoderForFactory = new TextEncoder()\nconst RAW_STREAM_FACTORY_CONSTRUCTOR_TEXT = (\n stream: ReturnType<typeof createStream>,\n) => {\n return new ReadableStream<Uint8Array>({\n start(controller) {\n stream.on({\n next(value: string | { $b64: string }) {\n try {\n if (typeof value === 'string') {\n controller.enqueue(textEncoderForFactory.encode(value))\n } else {\n controller.enqueue(base64ToUint8Array(value.$b64))\n }\n } catch {\n // Stream may be closed\n }\n },\n throw(error: unknown) {\n controller.error(error)\n },\n return() {\n try {\n controller.close()\n } catch {\n // Stream may already be closed\n }\n },\n })\n },\n })\n}\n\n// Minified factory function for binary mode - all chunks are base64 strings\n// This must be self-contained since it's injected into the HTML\nconst FACTORY_BINARY = `(s=>new ReadableStream({start(c){s.on({next(b){try{const d=atob(b),a=new Uint8Array(d.length);for(let i=0;i<d.length;i++)a[i]=d.charCodeAt(i);c.enqueue(a)}catch(_){}},throw(e){c.error(e)},return(){try{c.close()}catch(_){}}})}}))`\n\n// Minified factory function for text mode - chunks are string or {$b64: string}\n// Uses cached TextEncoder for performance\nconst FACTORY_TEXT = `(s=>{const e=new TextEncoder();return new ReadableStream({start(c){s.on({next(v){try{if(typeof v==='string'){c.enqueue(e.encode(v))}else{const d=atob(v.$b64),a=new Uint8Array(d.length);for(let i=0;i<d.length;i++)a[i]=d.charCodeAt(i);c.enqueue(a)}}catch(_){}},throw(x){c.error(x)},return(){try{c.close()}catch(_){}}})}})})`\n\n// Convert ReadableStream<Uint8Array> to seroval stream with base64-encoded chunks (binary mode)\nfunction toBinaryStream(readable: ReadableStream<Uint8Array>) {\n const stream = createStream()\n const reader = readable.getReader()\n\n // Use iterative loop instead of recursive async to avoid stack accumulation\n ;(async () => {\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) {\n stream.return(undefined)\n break\n }\n stream.next(uint8ArrayToBase64(value))\n }\n } catch (error) {\n stream.throw(error)\n } finally {\n reader.releaseLock()\n }\n })()\n\n return stream\n}\n\n// Convert ReadableStream<Uint8Array> to seroval stream with UTF-8 first, base64 fallback (text mode)\nfunction toTextStream(readable: ReadableStream<Uint8Array>) {\n const stream = createStream()\n const reader = readable.getReader()\n const decoder = new TextDecoder('utf-8', { fatal: true })\n\n // Use iterative loop instead of recursive async to avoid stack accumulation\n ;(async () => {\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) {\n // Flush any remaining bytes in the decoder\n try {\n const remaining = decoder.decode()\n if (remaining.length > 0) {\n stream.next(remaining)\n }\n } catch {\n // Ignore decode errors on flush\n }\n stream.return(undefined)\n break\n }\n\n try {\n // Try UTF-8 decode first\n const text = decoder.decode(value, { stream: true })\n if (text.length > 0) {\n stream.next(text)\n }\n } catch {\n // UTF-8 decode failed, fallback to base64\n stream.next({ $b64: uint8ArrayToBase64(value) })\n }\n }\n } catch (error) {\n stream.throw(error)\n } finally {\n reader.releaseLock()\n }\n })()\n\n return stream\n}\n\n// Factory plugin for binary mode\nconst RawStreamFactoryBinaryPlugin = /* @__PURE__ */ createPlugin<\n Record<string, never>,\n PluginInfo\n>({\n tag: 'tss/RawStreamFactory',\n test(value) {\n return value === RAW_STREAM_FACTORY_BINARY\n },\n parse: {\n sync(_value, _ctx, _data) {\n return {}\n },\n async async(_value, _ctx, _data) {\n return {}\n },\n stream(_value, _ctx, _data) {\n return {}\n },\n },\n serialize(_node, _ctx, _data) {\n return FACTORY_BINARY\n },\n deserialize(_node, _ctx, _data) {\n return RAW_STREAM_FACTORY_BINARY\n },\n})\n\n// Factory plugin for text mode\nconst RawStreamFactoryTextPlugin = /* @__PURE__ */ createPlugin<\n Record<string, never>,\n PluginInfo\n>({\n tag: 'tss/RawStreamFactoryText',\n test(value) {\n return value === RAW_STREAM_FACTORY_TEXT\n },\n parse: {\n sync(_value, _ctx, _data) {\n return {}\n },\n async async(_value, _ctx, _data) {\n return {}\n },\n stream(_value, _ctx, _data) {\n return {}\n },\n },\n serialize(_node, _ctx, _data) {\n return FACTORY_TEXT\n },\n deserialize(_node, _ctx, _data) {\n return RAW_STREAM_FACTORY_TEXT\n },\n})\n\nexport interface RawStreamSSRNode extends PluginInfo {\n hint: SerovalNode\n factory: SerovalNode\n stream: SerovalNode\n}\n\nexport interface RawStreamRPCNode extends PluginInfo {\n streamId: SerovalNode\n}\n\n/**\n * SSR Plugin - uses base64 or UTF-8+base64 encoding for chunks, delegates to seroval's stream mechanism.\n * Used during SSR when serializing to JavaScript code for HTML injection.\n *\n * Supports two modes based on RawStream hint:\n * - 'binary': Always base64 encode (default)\n * - 'text': Try UTF-8 first, fallback to base64 for invalid UTF-8\n */\nexport const RawStreamSSRPlugin = /* @__PURE__ */ createPlugin<\n RawStream,\n RawStreamSSRNode\n>({\n tag: 'tss/RawStream',\n extends: [RawStreamFactoryBinaryPlugin, RawStreamFactoryTextPlugin],\n\n test(value: unknown) {\n return value instanceof RawStream\n },\n\n parse: {\n sync(value: RawStream, ctx, _data) {\n // Sync parse not really supported for streams, return empty stream\n const factory =\n value.hint === 'text'\n ? RAW_STREAM_FACTORY_TEXT\n : RAW_STREAM_FACTORY_BINARY\n return {\n hint: ctx.parse(value.hint),\n factory: ctx.parse(factory),\n stream: ctx.parse(createStream()),\n }\n },\n async async(value: RawStream, ctx, _data) {\n const factory =\n value.hint === 'text'\n ? RAW_STREAM_FACTORY_TEXT\n : RAW_STREAM_FACTORY_BINARY\n const encodedStream =\n value.hint === 'text'\n ? toTextStream(value.stream)\n : toBinaryStream(value.stream)\n return {\n hint: await ctx.parse(value.hint),\n factory: await ctx.parse(factory),\n stream: await ctx.parse(encodedStream),\n }\n },\n stream(value: RawStream, ctx, _data) {\n const factory =\n value.hint === 'text'\n ? RAW_STREAM_FACTORY_TEXT\n : RAW_STREAM_FACTORY_BINARY\n const encodedStream =\n value.hint === 'text'\n ? toTextStream(value.stream)\n : toBinaryStream(value.stream)\n return {\n hint: ctx.parse(value.hint),\n factory: ctx.parse(factory),\n stream: ctx.parse(encodedStream),\n }\n },\n },\n\n serialize(node: RawStreamSSRNode, ctx, _data) {\n return (\n '(' +\n ctx.serialize(node.factory) +\n ')(' +\n ctx.serialize(node.stream) +\n ')'\n )\n },\n\n deserialize(node: RawStreamSSRNode, ctx, _data): any {\n const stream: ReturnType<typeof createStream> = ctx.deserialize(node.stream)\n const hint = ctx.deserialize(node.hint)\n return hint === 'text'\n ? RAW_STREAM_FACTORY_CONSTRUCTOR_TEXT(stream)\n : RAW_STREAM_FACTORY_CONSTRUCTOR_BINARY(stream)\n },\n})\n\n/**\n * Creates an RPC plugin instance that registers raw streams with a multiplexer.\n * Used for server function responses where we want binary framing.\n * Note: RPC always uses binary framing regardless of hint.\n *\n * @param onRawStream Callback invoked when a RawStream is encountered during serialization\n */\n/* @__NO_SIDE_EFFECTS__ */\nexport function createRawStreamRPCPlugin(onRawStream: OnRawStreamCallback) {\n // Own stream counter - sequential IDs starting at 1, independent of seroval internals\n let nextStreamId = 1\n\n return /* @__PURE__ */ createPlugin<RawStream, RawStreamRPCNode>({\n tag: 'tss/RawStream',\n\n test(value: unknown) {\n return value instanceof RawStream\n },\n\n parse: {\n async async(value: RawStream, ctx, _data: PluginData) {\n const streamId = nextStreamId++\n onRawStream(streamId, value.stream)\n return { streamId: await ctx.parse(streamId) }\n },\n stream(value: RawStream, ctx, _data: PluginData) {\n const streamId = nextStreamId++\n onRawStream(streamId, value.stream)\n return { streamId: ctx.parse(streamId) }\n },\n },\n\n serialize(): never {\n // RPC uses toCrossJSONStream which produces JSON nodes, not JS code.\n // This method is only called by crossSerialize* which we don't use.\n throw new Error(\n 'RawStreamRPCPlugin.serialize should not be called. RPC uses JSON serialization, not JS code generation.',\n )\n },\n\n deserialize(): never {\n // Client uses createRawStreamDeserializePlugin instead\n throw new Error(\n 'RawStreamRPCPlugin.deserialize should not be called. Use createRawStreamDeserializePlugin on client.',\n )\n },\n })\n}\n\n/**\n * Creates a deserialize-only plugin for client-side stream reconstruction.\n * Used in serverFnFetcher to wire up streams from frame decoder.\n *\n * @param getOrCreateStream Function to get/create a stream by ID from frame decoder\n */\nexport function createRawStreamDeserializePlugin(\n getOrCreateStream: (id: number) => ReadableStream<Uint8Array>,\n) {\n return /* @__PURE__ */ createPlugin<any, RawStreamRPCNode>({\n tag: 'tss/RawStream',\n\n test: () => false, // Client never serializes RawStream\n\n parse: {}, // Client only deserializes, never parses\n\n serialize(): never {\n // Client never serializes RawStream back to server\n throw new Error(\n 'RawStreamDeserializePlugin.serialize should not be called. Client only deserializes.',\n )\n },\n\n deserialize(node, ctx, _data) {\n // In normal seroval usage, ctx.deserialize exists.\n // Some unit tests call plugin.deserialize directly with a minimal ctx.\n const id =\n typeof (ctx as any)?.deserialize === 'function'\n ? (ctx as any).deserialize(node.streamId)\n : (node as any).streamId\n return getOrCreateStream(id as number)\n },\n })\n}\n"],"mappings":";;;;;;;;;;;;;;;AAmCA,IAAa,YAAb,MAAuB;CAGrB,YACE,QACA,SACA;AAFgB,OAAA,SAAA;AAGhB,OAAK,OAAO,SAAS,QAAQ;;;AAcjC,MAAM,aAAmB,WAAmB;AAC5C,MAAM,gBAAgB,CAAC,CAAC,cAAc,OAAO,WAAW,SAAS;AAEjE,SAAS,mBAAmB,OAA2B;AACrD,KAAI,MAAM,WAAW,EAAG,QAAO;AAE/B,KAAI,cACF,QAAO,WAAW,KAAK,MAAM,CAAC,SAAS,SAAS;CAIlD,MAAM,aAAa;CACnB,MAAM,SAAwB,EAAE;AAChC,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,YAAY;EACjD,MAAM,QAAQ,MAAM,SAAS,GAAG,IAAI,WAAW;AAC/C,SAAO,KAAK,OAAO,aAAa,MAAM,MAAM,MAAa,CAAC;;AAE5D,QAAO,KAAK,OAAO,KAAK,GAAG,CAAC;;AAG9B,SAAS,mBAAmB,QAA4B;AACtD,KAAI,OAAO,WAAW,EAAG,QAAO,IAAI,WAAW,EAAE;AAEjD,KAAI,eAAe;EACjB,MAAM,MAAM,WAAW,KAAK,QAAQ,SAAS;AAC7C,SAAO,IAAI,WAAW,IAAI,QAAQ,IAAI,YAAY,IAAI,WAAW;;CAGnE,MAAM,SAAS,KAAK,OAAO;CAC3B,MAAM,QAAQ,IAAI,WAAW,OAAO,OAAO;AAC3C,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,IACjC,OAAM,KAAK,OAAO,WAAW,EAAE;AAEjC,QAAO;;AAIT,MAAM,4BAAmD,OAAO,OAAO,KAAK;AAC5E,MAAM,0BAAiD,OAAO,OAAO,KAAK;AAI1E,MAAM,yCACJ,WAEA,IAAI,eAA2B,EAC7B,MAAM,YAAY;AAChB,QAAO,GAAG;EACR,KAAK,QAAgB;AACnB,OAAI;AACF,eAAW,QAAQ,mBAAmB,OAAO,CAAC;WACxC;;EAIV,MAAM,OAAgB;AACpB,cAAW,MAAM,MAAM;;EAEzB,SAAS;AACP,OAAI;AACF,eAAW,OAAO;WACZ;;EAIX,CAAC;GAEL,CAAC;AAKJ,MAAM,wBAAwB,IAAI,aAAa;AAC/C,MAAM,uCACJ,WACG;AACH,QAAO,IAAI,eAA2B,EACpC,MAAM,YAAY;AAChB,SAAO,GAAG;GACR,KAAK,OAAkC;AACrC,QAAI;AACF,SAAI,OAAO,UAAU,SACnB,YAAW,QAAQ,sBAAsB,OAAO,MAAM,CAAC;SAEvD,YAAW,QAAQ,mBAAmB,MAAM,KAAK,CAAC;YAE9C;;GAIV,MAAM,OAAgB;AACpB,eAAW,MAAM,MAAM;;GAEzB,SAAS;AACP,QAAI;AACF,gBAAW,OAAO;YACZ;;GAIX,CAAC;IAEL,CAAC;;AAKJ,MAAM,iBAAiB;AAIvB,MAAM,eAAe;AAGrB,SAAS,eAAe,UAAsC;CAC5D,MAAM,SAAS,cAAc;CAC7B,MAAM,SAAS,SAAS,WAAW;AAGlC,EAAC,YAAY;AACZ,MAAI;AACF,UAAO,MAAM;IACX,MAAM,EAAE,MAAM,UAAU,MAAM,OAAO,MAAM;AAC3C,QAAI,MAAM;AACR,YAAO,OAAO,KAAA,EAAU;AACxB;;AAEF,WAAO,KAAK,mBAAmB,MAAM,CAAC;;WAEjC,OAAO;AACd,UAAO,MAAM,MAAM;YACX;AACR,UAAO,aAAa;;KAEpB;AAEJ,QAAO;;AAIT,SAAS,aAAa,UAAsC;CAC1D,MAAM,SAAS,cAAc;CAC7B,MAAM,SAAS,SAAS,WAAW;CACnC,MAAM,UAAU,IAAI,YAAY,SAAS,EAAE,OAAO,MAAM,CAAC;AAGxD,EAAC,YAAY;AACZ,MAAI;AACF,UAAO,MAAM;IACX,MAAM,EAAE,MAAM,UAAU,MAAM,OAAO,MAAM;AAC3C,QAAI,MAAM;AAER,SAAI;MACF,MAAM,YAAY,QAAQ,QAAQ;AAClC,UAAI,UAAU,SAAS,EACrB,QAAO,KAAK,UAAU;aAElB;AAGR,YAAO,OAAO,KAAA,EAAU;AACxB;;AAGF,QAAI;KAEF,MAAM,OAAO,QAAQ,OAAO,OAAO,EAAE,QAAQ,MAAM,CAAC;AACpD,SAAI,KAAK,SAAS,EAChB,QAAO,KAAK,KAAK;YAEb;AAEN,YAAO,KAAK,EAAE,MAAM,mBAAmB,MAAM,EAAE,CAAC;;;WAG7C,OAAO;AACd,UAAO,MAAM,MAAM;YACX;AACR,UAAO,aAAa;;KAEpB;AAEJ,QAAO;;;;;;;;;;AA6ET,MAAa,qBAAqC,6BAGhD;CACA,KAAK;CACL,SAAS,CA9E0C,6BAGnD;EACA,KAAK;EACL,KAAK,OAAO;AACV,UAAO,UAAU;;EAEnB,OAAO;GACL,KAAK,QAAQ,MAAM,OAAO;AACxB,WAAO,EAAE;;GAEX,MAAM,MAAM,QAAQ,MAAM,OAAO;AAC/B,WAAO,EAAE;;GAEX,OAAO,QAAQ,MAAM,OAAO;AAC1B,WAAO,EAAE;;GAEZ;EACD,UAAU,OAAO,MAAM,OAAO;AAC5B,UAAO;;EAET,YAAY,OAAO,MAAM,OAAO;AAC9B,UAAO;;EAEV,CAAC,EAGiD,6BAGjD;EACA,KAAK;EACL,KAAK,OAAO;AACV,UAAO,UAAU;;EAEnB,OAAO;GACL,KAAK,QAAQ,MAAM,OAAO;AACxB,WAAO,EAAE;;GAEX,MAAM,MAAM,QAAQ,MAAM,OAAO;AAC/B,WAAO,EAAE;;GAEX,OAAO,QAAQ,MAAM,OAAO;AAC1B,WAAO,EAAE;;GAEZ;EACD,UAAU,OAAO,MAAM,OAAO;AAC5B,UAAO;;EAET,YAAY,OAAO,MAAM,OAAO;AAC9B,UAAO;;EAEV,CAAC,CAyBmE;CAEnE,KAAK,OAAgB;AACnB,SAAO,iBAAiB;;CAG1B,OAAO;EACL,KAAK,OAAkB,KAAK,OAAO;GAEjC,MAAM,UACJ,MAAM,SAAS,SACX,0BACA;AACN,UAAO;IACL,MAAM,IAAI,MAAM,MAAM,KAAK;IAC3B,SAAS,IAAI,MAAM,QAAQ;IAC3B,QAAQ,IAAI,MAAM,cAAc,CAAC;IAClC;;EAEH,MAAM,MAAM,OAAkB,KAAK,OAAO;GACxC,MAAM,UACJ,MAAM,SAAS,SACX,0BACA;GACN,MAAM,gBACJ,MAAM,SAAS,SACX,aAAa,MAAM,OAAO,GAC1B,eAAe,MAAM,OAAO;AAClC,UAAO;IACL,MAAM,MAAM,IAAI,MAAM,MAAM,KAAK;IACjC,SAAS,MAAM,IAAI,MAAM,QAAQ;IACjC,QAAQ,MAAM,IAAI,MAAM,cAAc;IACvC;;EAEH,OAAO,OAAkB,KAAK,OAAO;GACnC,MAAM,UACJ,MAAM,SAAS,SACX,0BACA;GACN,MAAM,gBACJ,MAAM,SAAS,SACX,aAAa,MAAM,OAAO,GAC1B,eAAe,MAAM,OAAO;AAClC,UAAO;IACL,MAAM,IAAI,MAAM,MAAM,KAAK;IAC3B,SAAS,IAAI,MAAM,QAAQ;IAC3B,QAAQ,IAAI,MAAM,cAAc;IACjC;;EAEJ;CAED,UAAU,MAAwB,KAAK,OAAO;AAC5C,SACE,MACA,IAAI,UAAU,KAAK,QAAQ,GAC3B,OACA,IAAI,UAAU,KAAK,OAAO,GAC1B;;CAIJ,YAAY,MAAwB,KAAK,OAAY;EACnD,MAAM,SAA0C,IAAI,YAAY,KAAK,OAAO;AAE5E,SADa,IAAI,YAAY,KAAK,KAAK,KACvB,SACZ,oCAAoC,OAAO,GAC3C,sCAAsC,OAAO;;CAEpD,CAAC;;;;;;;;;AAUF,SAAgB,yBAAyB,aAAkC;CAEzE,IAAI,eAAe;AAEnB,QAAuB,6BAA0C;EAC/D,KAAK;EAEL,KAAK,OAAgB;AACnB,UAAO,iBAAiB;;EAG1B,OAAO;GACL,MAAM,MAAM,OAAkB,KAAK,OAAmB;IACpD,MAAM,WAAW;AACjB,gBAAY,UAAU,MAAM,OAAO;AACnC,WAAO,EAAE,UAAU,MAAM,IAAI,MAAM,SAAS,EAAE;;GAEhD,OAAO,OAAkB,KAAK,OAAmB;IAC/C,MAAM,WAAW;AACjB,gBAAY,UAAU,MAAM,OAAO;AACnC,WAAO,EAAE,UAAU,IAAI,MAAM,SAAS,EAAE;;GAE3C;EAED,YAAmB;AAGjB,SAAM,IAAI,MACR,0GACD;;EAGH,cAAqB;AAEnB,SAAM,IAAI,MACR,uGACD;;EAEJ,CAAC;;;;;;;;AASJ,SAAgB,iCACd,mBACA;AACA,QAAuB,6BAAoC;EACzD,KAAK;EAEL,YAAY;EAEZ,OAAO,EAAE;EAET,YAAmB;AAEjB,SAAM,IAAI,MACR,uFACD;;EAGH,YAAY,MAAM,KAAK,OAAO;AAO5B,UAAO,kBAHL,OAAQ,KAAa,gBAAgB,aAChC,IAAY,YAAY,KAAK,SAAS,GACtC,KAAa,SACkB;;EAEzC,CAAC"}
@@ -4,7 +4,7 @@ import { createPlugin } from "seroval";
4
4
  * this plugin serializes only the `message` part of an Error
5
5
  * this helps with serializing e.g. a ZodError which has functions attached that cannot be serialized
6
6
  */
7
- var ShallowErrorPlugin = /* @__PURE__ */ createPlugin({
7
+ const ShallowErrorPlugin = /* @__PURE__ */ createPlugin({
8
8
  tag: "$TSR/Error",
9
9
  test(value) {
10
10
  return value instanceof Error;
@@ -1 +1 @@
1
- {"version":3,"file":"ShallowErrorPlugin.js","names":[],"sources":["../../../../src/ssr/serializer/ShallowErrorPlugin.ts"],"sourcesContent":["import { createPlugin } from 'seroval'\nimport type { PluginInfo, SerovalNode } from 'seroval'\n\nexport interface ErrorNode extends PluginInfo {\n message: SerovalNode\n}\n\n/**\n * this plugin serializes only the `message` part of an Error\n * this helps with serializing e.g. a ZodError which has functions attached that cannot be serialized\n */\nexport const ShallowErrorPlugin = /* @__PURE__ */ createPlugin<\n Error,\n ErrorNode\n>({\n tag: '$TSR/Error',\n test(value) {\n return value instanceof Error\n },\n parse: {\n sync(value, ctx) {\n return {\n message: ctx.parse(value.message),\n }\n },\n async async(value, ctx) {\n return {\n message: await ctx.parse(value.message),\n }\n },\n stream(value, ctx) {\n return {\n message: ctx.parse(value.message),\n }\n },\n },\n serialize(node, ctx) {\n return 'new Error(' + ctx.serialize(node.message) + ')'\n },\n deserialize(node, ctx) {\n return new Error(ctx.deserialize(node.message))\n },\n})\n"],"mappings":";;;;;;AAWA,IAAa,qBAAqC,6BAGhD;CACA,KAAK;CACL,KAAK,OAAO;AACV,SAAO,iBAAiB;;CAE1B,OAAO;EACL,KAAK,OAAO,KAAK;AACf,UAAO,EACL,SAAS,IAAI,MAAM,MAAM,QAAQ,EAClC;;EAEH,MAAM,MAAM,OAAO,KAAK;AACtB,UAAO,EACL,SAAS,MAAM,IAAI,MAAM,MAAM,QAAQ,EACxC;;EAEH,OAAO,OAAO,KAAK;AACjB,UAAO,EACL,SAAS,IAAI,MAAM,MAAM,QAAQ,EAClC;;EAEJ;CACD,UAAU,MAAM,KAAK;AACnB,SAAO,eAAe,IAAI,UAAU,KAAK,QAAQ,GAAG;;CAEtD,YAAY,MAAM,KAAK;AACrB,SAAO,IAAI,MAAM,IAAI,YAAY,KAAK,QAAQ,CAAC;;CAElD,CAAC"}
1
+ {"version":3,"file":"ShallowErrorPlugin.js","names":[],"sources":["../../../../src/ssr/serializer/ShallowErrorPlugin.ts"],"sourcesContent":["import { createPlugin } from 'seroval'\nimport type { PluginInfo, SerovalNode } from 'seroval'\n\nexport interface ErrorNode extends PluginInfo {\n message: SerovalNode\n}\n\n/**\n * this plugin serializes only the `message` part of an Error\n * this helps with serializing e.g. a ZodError which has functions attached that cannot be serialized\n */\nexport const ShallowErrorPlugin = /* @__PURE__ */ createPlugin<\n Error,\n ErrorNode\n>({\n tag: '$TSR/Error',\n test(value) {\n return value instanceof Error\n },\n parse: {\n sync(value, ctx) {\n return {\n message: ctx.parse(value.message),\n }\n },\n async async(value, ctx) {\n return {\n message: await ctx.parse(value.message),\n }\n },\n stream(value, ctx) {\n return {\n message: ctx.parse(value.message),\n }\n },\n },\n serialize(node, ctx) {\n return 'new Error(' + ctx.serialize(node.message) + ')'\n },\n deserialize(node, ctx) {\n return new Error(ctx.deserialize(node.message))\n },\n})\n"],"mappings":";;;;;;AAWA,MAAa,qBAAqC,6BAGhD;CACA,KAAK;CACL,KAAK,OAAO;AACV,SAAO,iBAAiB;;CAE1B,OAAO;EACL,KAAK,OAAO,KAAK;AACf,UAAO,EACL,SAAS,IAAI,MAAM,MAAM,QAAQ,EAClC;;EAEH,MAAM,MAAM,OAAO,KAAK;AACtB,UAAO,EACL,SAAS,MAAM,IAAI,MAAM,MAAM,QAAQ,EACxC;;EAEH,OAAO,OAAO,KAAK;AACjB,UAAO,EACL,SAAS,IAAI,MAAM,MAAM,QAAQ,EAClC;;EAEJ;CACD,UAAU,MAAM,KAAK;AACnB,SAAO,eAAe,IAAI,UAAU,KAAK,QAAQ,GAAG;;CAEtD,YAAY,MAAM,KAAK;AACrB,SAAO,IAAI,MAAM,IAAI,YAAY,KAAK,QAAQ,CAAC;;CAElD,CAAC"}
@@ -2,7 +2,7 @@ import { ShallowErrorPlugin } from "./ShallowErrorPlugin.js";
2
2
  import { RawStreamSSRPlugin } from "./RawStream.js";
3
3
  import { ReadableStreamPlugin } from "seroval-plugins/web";
4
4
  //#region src/ssr/serializer/seroval-plugins.ts
5
- var defaultSerovalPlugins = [
5
+ const defaultSerovalPlugins = [
6
6
  ShallowErrorPlugin,
7
7
  RawStreamSSRPlugin,
8
8
  ReadableStreamPlugin
@@ -1 +1 @@
1
- {"version":3,"file":"seroval-plugins.js","names":[],"sources":["../../../../src/ssr/serializer/seroval-plugins.ts"],"sourcesContent":["import { ReadableStreamPlugin } from 'seroval-plugins/web'\nimport { ShallowErrorPlugin } from './ShallowErrorPlugin'\nimport { RawStreamSSRPlugin } from './RawStream'\nimport type { RawStream } from './RawStream'\nimport type { Plugin } from 'seroval'\n\nexport const defaultSerovalPlugins = [\n ShallowErrorPlugin as Plugin<Error, any>,\n // RawStreamSSRPlugin must come before ReadableStreamPlugin to match first\n RawStreamSSRPlugin as Plugin<RawStream, any>,\n // ReadableStreamNode is not exported by seroval\n ReadableStreamPlugin as Plugin<ReadableStream, any>,\n]\n"],"mappings":";;;;AAMA,IAAa,wBAAwB;CACnC;CAEA;CAEA;CACD"}
1
+ {"version":3,"file":"seroval-plugins.js","names":[],"sources":["../../../../src/ssr/serializer/seroval-plugins.ts"],"sourcesContent":["import { ReadableStreamPlugin } from 'seroval-plugins/web'\nimport { ShallowErrorPlugin } from './ShallowErrorPlugin'\nimport { RawStreamSSRPlugin } from './RawStream'\nimport type { RawStream } from './RawStream'\nimport type { Plugin } from 'seroval'\n\nexport const defaultSerovalPlugins = [\n ShallowErrorPlugin as Plugin<Error, any>,\n // RawStreamSSRPlugin must come before ReadableStreamPlugin to match first\n RawStreamSSRPlugin as Plugin<RawStream, any>,\n // ReadableStreamNode is not exported by seroval\n ReadableStreamPlugin as Plugin<ReadableStream, any>,\n]\n"],"mappings":";;;;AAMA,MAAa,wBAAwB;CACnC;CAEA;CAEA;CACD"}
@@ -10,10 +10,10 @@ import { dehydrateSsrMatchId } from "./ssr-match-id.js";
10
10
  import tsrScript_default from "./tsrScript.js";
11
11
  import { crossSerializeStream, getCrossReferenceHeader } from "seroval";
12
12
  //#region src/ssr/ssr-server.ts
13
- var SCOPE_ID = "tsr";
14
- var TSR_PREFIX = GLOBAL_TSR + ".router=";
15
- var P_PREFIX = GLOBAL_TSR + ".p(()=>";
16
- var P_SUFFIX = ")";
13
+ const SCOPE_ID = "tsr";
14
+ const TSR_PREFIX = GLOBAL_TSR + ".router=";
15
+ const P_PREFIX = GLOBAL_TSR + ".p(()=>";
16
+ const P_SUFFIX = ")";
17
17
  function dehydrateMatch(match) {
18
18
  const dehydratedMatch = {
19
19
  i: dehydrateSsrMatchId(match.id),
@@ -29,7 +29,7 @@ function dehydrateMatch(match) {
29
29
  if (match.globalNotFound) dehydratedMatch.g = true;
30
30
  return dehydratedMatch;
31
31
  }
32
- var INITIAL_SCRIPTS = [getCrossReferenceHeader(SCOPE_ID), tsrScript_default];
32
+ const INITIAL_SCRIPTS = [getCrossReferenceHeader(SCOPE_ID), tsrScript_default];
33
33
  var ScriptBuffer = class {
34
34
  constructor(router) {
35
35
  this._scriptBarrierLifted = false;
@@ -93,10 +93,10 @@ var ScriptBuffer = class {
93
93
  this.router = void 0;
94
94
  }
95
95
  };
96
- var isProd = process.env.NODE_ENV === "production";
97
- var MANIFEST_CACHE_SIZE = 100;
98
- var manifestCaches = /* @__PURE__ */ new WeakMap();
99
- var inlineCssCaches = /* @__PURE__ */ new WeakMap();
96
+ const isProd = process.env.NODE_ENV === "production";
97
+ const MANIFEST_CACHE_SIZE = 100;
98
+ const manifestCaches = /* @__PURE__ */ new WeakMap();
99
+ const inlineCssCaches = /* @__PURE__ */ new WeakMap();
100
100
  function getManifestCache(manifest) {
101
101
  const cache = manifestCaches.get(manifest);
102
102
  if (cache) return cache;
@@ -1 +1 @@
1
- {"version":3,"file":"ssr-server.js","names":[],"sources":["../../../src/ssr/ssr-server.ts"],"sourcesContent":["import { crossSerializeStream, getCrossReferenceHeader } from 'seroval'\nimport { invariant } from '../invariant'\nimport {\n createInlineCssPlaceholderAsset,\n createInlineCssStyleAsset,\n getStylesheetHref,\n isInlinableStylesheet,\n} from '../manifest'\nimport { decodePath } from '../utils'\nimport { createLRUCache } from '../lru-cache'\nimport { rootRouteId } from '../root'\nimport minifiedTsrBootStrapScript from './tsrScript?script-string'\nimport { GLOBAL_TSR, TSR_SCRIPT_BARRIER_ID } from './constants'\nimport { dehydrateSsrMatchId } from './ssr-match-id'\nimport { defaultSerovalPlugins } from './serializer/seroval-plugins'\nimport { makeSsrSerovalPlugin } from './serializer/transformer'\nimport type { LRUCache } from '../lru-cache'\nimport type { DehydratedMatch, DehydratedRouter } from './types'\nimport type { AnySerializationAdapter } from './serializer/transformer'\nimport type { AnyRouter } from '../router'\nimport type { AnyRouteMatch } from '../Matches'\nimport type { Manifest, RouterManagedTag } from '../manifest'\n\ndeclare module '../router' {\n interface ServerSsr {\n setRenderFinished: () => void\n cleanup: () => void\n }\n interface RouterEvents {\n onInjectedHtml: {\n type: 'onInjectedHtml'\n }\n onSerializationFinished: {\n type: 'onSerializationFinished'\n }\n }\n}\n\nconst SCOPE_ID = 'tsr'\n\nconst TSR_PREFIX = GLOBAL_TSR + '.router='\nconst P_PREFIX = GLOBAL_TSR + '.p(()=>'\nconst P_SUFFIX = ')'\n\nexport function dehydrateMatch(match: AnyRouteMatch): DehydratedMatch {\n const dehydratedMatch: DehydratedMatch = {\n i: dehydrateSsrMatchId(match.id),\n u: match.updatedAt,\n s: match.status,\n }\n\n const properties = [\n ['__beforeLoadContext', 'b'],\n ['loaderData', 'l'],\n ['error', 'e'],\n ['ssr', 'ssr'],\n ] as const\n\n for (const [key, shorthand] of properties) {\n if (match[key] !== undefined) {\n dehydratedMatch[shorthand] = match[key]\n }\n }\n if (match.globalNotFound) {\n dehydratedMatch.g = true\n }\n return dehydratedMatch\n}\n\nconst INITIAL_SCRIPTS = [\n getCrossReferenceHeader(SCOPE_ID),\n minifiedTsrBootStrapScript,\n]\n\nclass ScriptBuffer {\n private router: AnyRouter | undefined\n private _queue: Array<string>\n private _scriptBarrierLifted = false\n private _cleanedUp = false\n private _pendingMicrotask = false\n\n constructor(router: AnyRouter) {\n this.router = router\n // Copy INITIAL_SCRIPTS to avoid mutating the shared array\n this._queue = INITIAL_SCRIPTS.slice()\n }\n\n enqueue(script: string) {\n if (this._cleanedUp) return\n this._queue.push(script)\n // If barrier is lifted, schedule injection (if not already scheduled)\n if (this._scriptBarrierLifted && !this._pendingMicrotask) {\n this._pendingMicrotask = true\n queueMicrotask(() => {\n this._pendingMicrotask = false\n this.injectBufferedScripts()\n })\n }\n }\n\n liftBarrier() {\n if (this._scriptBarrierLifted || this._cleanedUp) return\n this._scriptBarrierLifted = true\n if (this._queue.length > 0 && !this._pendingMicrotask) {\n this._pendingMicrotask = true\n queueMicrotask(() => {\n this._pendingMicrotask = false\n this.injectBufferedScripts()\n })\n }\n }\n\n /**\n * Flushes any pending scripts synchronously.\n * Call this before emitting onSerializationFinished to ensure all scripts are injected.\n *\n * IMPORTANT: Only injects if the barrier has been lifted. Before the barrier is lifted,\n * scripts should remain in the queue so takeBufferedScripts() can retrieve them\n */\n flush() {\n if (!this._scriptBarrierLifted) return\n if (this._cleanedUp) return\n this._pendingMicrotask = false\n const scriptsToInject = this.takeAll()\n if (scriptsToInject && this.router?.serverSsr) {\n this.router.serverSsr.injectScript(scriptsToInject)\n }\n }\n\n takeAll() {\n const bufferedScripts = this._queue\n this._queue = []\n if (bufferedScripts.length === 0) {\n return undefined\n }\n // Optimization: if only one script, avoid join\n if (bufferedScripts.length === 1) {\n return bufferedScripts[0] + ';document.currentScript.remove()'\n }\n // Append cleanup script and join - avoid push() to not mutate then iterate\n return bufferedScripts.join(';') + ';document.currentScript.remove()'\n }\n\n injectBufferedScripts() {\n if (this._cleanedUp) return\n // Early return if queue is empty (avoids unnecessary takeAll() call)\n if (this._queue.length === 0) return\n const scriptsToInject = this.takeAll()\n if (scriptsToInject && this.router?.serverSsr) {\n this.router.serverSsr.injectScript(scriptsToInject)\n }\n }\n\n cleanup() {\n this._cleanedUp = true\n this._queue = []\n this.router = undefined\n }\n}\n\nconst isProd = process.env.NODE_ENV === 'production'\n\ntype FilteredRoutes = Manifest['routes']\n\ntype ManifestLRU = LRUCache<string, FilteredRoutes>\ntype InlineCssLRU = LRUCache<string, string>\n\nconst MANIFEST_CACHE_SIZE = 100\nconst manifestCaches = new WeakMap<Manifest, ManifestLRU>()\nconst inlineCssCaches = new WeakMap<Manifest, InlineCssLRU>()\n\nfunction getManifestCache(manifest: Manifest): ManifestLRU {\n const cache = manifestCaches.get(manifest)\n if (cache) return cache\n const newCache = createLRUCache<string, FilteredRoutes>(MANIFEST_CACHE_SIZE)\n manifestCaches.set(manifest, newCache)\n return newCache\n}\n\nfunction getInlineCssCache(manifest: Manifest): InlineCssLRU {\n const cache = inlineCssCaches.get(manifest)\n if (cache) return cache\n const newCache = createLRUCache<string, string>(MANIFEST_CACHE_SIZE)\n inlineCssCaches.set(manifest, newCache)\n return newCache\n}\n\nfunction getInlineCssHrefsForMatches(\n manifest: Manifest | undefined,\n matches: Array<AnyRouteMatch>,\n) {\n const styles = manifest?.inlineCss?.styles\n if (!styles) return []\n\n const seen = new Set<string>()\n const hrefs: Array<string> = []\n\n for (const match of matches) {\n const assets = manifest?.routes[match.routeId]?.assets ?? []\n for (const asset of assets) {\n const href = getStylesheetHref(asset)\n if (!href || seen.has(href) || styles[href] === undefined) {\n continue\n }\n seen.add(href)\n hrefs.push(href)\n }\n }\n\n return hrefs\n}\n\nfunction getInlineCssForHrefs(manifest: Manifest, hrefs: Array<string>) {\n const styles = manifest.inlineCss?.styles\n if (!styles || hrefs.length === 0) return undefined\n\n const cacheKey = hrefs.join('\\0')\n if (isProd) {\n const cached = getInlineCssCache(manifest).get(cacheKey)\n if (cached !== undefined) return cached\n }\n\n const css = hrefs.map((href) => styles[href]!).join('')\n\n if (isProd) {\n getInlineCssCache(manifest).set(cacheKey, css)\n }\n\n return css\n}\n\nfunction getInlineCssAssetForMatches(\n manifest: Manifest | undefined,\n matches: Array<AnyRouteMatch>,\n) {\n if (!manifest?.inlineCss) return undefined\n\n const hrefs = getInlineCssHrefsForMatches(manifest, matches)\n const css = getInlineCssForHrefs(manifest, hrefs)\n\n return css === undefined ? undefined : createInlineCssStyleAsset(css)\n}\n\nfunction stripInlinedStylesheetAssets(\n manifest: Manifest,\n routes: FilteredRoutes,\n matches: Array<AnyRouteMatch>,\n): FilteredRoutes {\n if (!manifest.inlineCss) {\n return routes\n }\n\n const nextRoutes: FilteredRoutes = {}\n\n for (const [routeId, route] of Object.entries(routes)) {\n const assets = route.assets?.filter(\n (asset) => !isInlinableStylesheet(manifest, asset),\n )\n\n const nextRoute = { ...route }\n if (assets) {\n if (assets.length > 0) {\n nextRoute.assets = assets\n } else {\n delete nextRoute.assets\n }\n }\n nextRoutes[routeId] = nextRoute\n }\n\n if (getInlineCssAssetForMatches(manifest, matches)) {\n const rootRoute = nextRoutes[rootRouteId] ?? {}\n nextRoutes[rootRouteId] = {\n ...rootRoute,\n assets: [createInlineCssPlaceholderAsset(), ...(rootRoute.assets ?? [])],\n }\n }\n\n return nextRoutes\n}\n\nexport function attachRouterServerSsrUtils({\n router,\n manifest,\n getRequestAssets,\n includeUnmatchedRouteAssets = true,\n}: {\n router: AnyRouter\n manifest: Manifest | undefined\n getRequestAssets?: () => Array<RouterManagedTag> | undefined\n includeUnmatchedRouteAssets?: boolean\n}) {\n router.ssr = {\n get manifest() {\n const requestAssets = getRequestAssets?.()\n const inlineCssAsset = getInlineCssAssetForMatches(\n manifest,\n router.stores.matches.get(),\n )\n if (!requestAssets?.length && !inlineCssAsset) return manifest\n // Merge request-scoped assets into root route without mutating cached manifest\n return {\n ...manifest,\n routes: {\n ...manifest?.routes,\n [rootRouteId]: {\n ...manifest?.routes?.[rootRouteId],\n assets: [\n ...(requestAssets ?? []),\n ...(inlineCssAsset ? [inlineCssAsset] : []),\n ...(manifest?.routes?.[rootRouteId]?.assets ?? []),\n ],\n },\n },\n }\n },\n }\n let _dehydrated = false\n let _serializationFinished = false\n const renderFinishedListeners: Array<() => void> = []\n const serializationFinishedListeners: Array<() => void> = []\n const scriptBuffer = new ScriptBuffer(router)\n let injectedHtmlBuffer = ''\n\n router.serverSsr = {\n injectHtml: (html: string) => {\n if (!html) return\n // Buffer the HTML so it can be retrieved via takeBufferedHtml()\n injectedHtmlBuffer += html\n // Emit event to notify subscribers that new HTML is available\n router.emit({\n type: 'onInjectedHtml',\n })\n },\n injectScript: (script: string) => {\n if (!script) return\n const html = `<script${router.options.ssr?.nonce ? ` nonce='${router.options.ssr.nonce}'` : ''}>${script}</script>`\n router.serverSsr!.injectHtml(html)\n },\n dehydrate: async (opts?: { requestAssets?: Array<RouterManagedTag> }) => {\n if (_dehydrated) {\n if (process.env.NODE_ENV !== 'production') {\n throw new Error('Invariant failed: router is already dehydrated!')\n }\n\n invariant()\n }\n let matchesToDehydrate = router.stores.matches.get()\n if (router.isShell()) {\n // In SPA mode we only want to dehydrate the root match\n matchesToDehydrate = matchesToDehydrate.slice(0, 1)\n }\n const matches = matchesToDehydrate.map(dehydrateMatch)\n\n let manifestToDehydrate: Manifest | undefined = undefined\n // For currently matched routes, send full manifest (preloads + assets).\n // For unmatched routes, include assets only when includeUnmatchedRouteAssets\n // is true; otherwise omit them entirely. Preloads for unmatched routes are\n // still excluded because they are handled via dynamic imports.\n if (manifest) {\n // Prod-only caching; in dev manifests may be replaced/updated (HMR)\n const currentRouteIdsList = matchesToDehydrate.map((m) => m.routeId)\n const manifestCacheKey = `${currentRouteIdsList.join('\\0')}\\0includeUnmatchedRouteAssets=${includeUnmatchedRouteAssets}`\n\n let filteredRoutes: FilteredRoutes | undefined\n\n if (isProd) {\n filteredRoutes = getManifestCache(manifest).get(manifestCacheKey)\n }\n\n if (!filteredRoutes) {\n const currentRouteIds = new Set(currentRouteIdsList)\n const nextFilteredRoutes: FilteredRoutes = {}\n\n for (const routeId in manifest.routes) {\n const routeManifest = manifest.routes[routeId]!\n if (currentRouteIds.has(routeId)) {\n nextFilteredRoutes[routeId] = routeManifest\n } else if (\n includeUnmatchedRouteAssets &&\n routeManifest.assets &&\n routeManifest.assets.length > 0\n ) {\n nextFilteredRoutes[routeId] = {\n assets: routeManifest.assets,\n }\n }\n }\n\n filteredRoutes = stripInlinedStylesheetAssets(\n manifest,\n nextFilteredRoutes,\n matchesToDehydrate,\n )\n\n if (isProd) {\n getManifestCache(manifest).set(manifestCacheKey, filteredRoutes)\n }\n }\n\n manifestToDehydrate = {\n routes: { ...filteredRoutes },\n }\n\n // Merge request-scoped assets into root route (without mutating cached manifest)\n if (opts?.requestAssets?.length) {\n const existingRoot = manifestToDehydrate.routes[rootRouteId]\n manifestToDehydrate.routes[rootRouteId] = {\n ...existingRoot,\n assets: [...opts.requestAssets, ...(existingRoot?.assets ?? [])],\n }\n }\n }\n const dehydratedRouter: DehydratedRouter = {\n manifest: manifestToDehydrate,\n matches,\n }\n const lastMatchId = matchesToDehydrate[matchesToDehydrate.length - 1]?.id\n if (lastMatchId) {\n dehydratedRouter.lastMatchId = dehydrateSsrMatchId(lastMatchId)\n }\n const dehydratedData = await router.options.dehydrate?.()\n if (dehydratedData) {\n dehydratedRouter.dehydratedData = dehydratedData\n }\n _dehydrated = true\n\n const trackPlugins = { didRun: false }\n const serializationAdapters = router.options.serializationAdapters as\n | Array<AnySerializationAdapter>\n | undefined\n const plugins = serializationAdapters\n ? serializationAdapters\n .map((t) => makeSsrSerovalPlugin(t, trackPlugins))\n .concat(defaultSerovalPlugins)\n : defaultSerovalPlugins\n\n const signalSerializationComplete = () => {\n _serializationFinished = true\n try {\n serializationFinishedListeners.forEach((l) => l())\n router.emit({ type: 'onSerializationFinished' })\n } catch (err) {\n console.error('Serialization listener error:', err)\n } finally {\n serializationFinishedListeners.length = 0\n renderFinishedListeners.length = 0\n }\n }\n\n crossSerializeStream(dehydratedRouter, {\n refs: new Map(),\n plugins,\n onSerialize: (data, initial) => {\n let serialized = initial ? TSR_PREFIX + data : data\n if (trackPlugins.didRun) {\n serialized = P_PREFIX + serialized + P_SUFFIX\n }\n scriptBuffer.enqueue(serialized)\n },\n onError: (err: unknown) => {\n console.error('Serialization error:', err)\n if (err && (err as any).stack) {\n console.error((err as any).stack)\n }\n signalSerializationComplete()\n },\n scopeId: SCOPE_ID,\n onDone: () => {\n scriptBuffer.enqueue(GLOBAL_TSR + '.e()')\n // Flush all pending scripts synchronously before signaling completion\n // This ensures all scripts are injected before onSerializationFinished is emitted\n scriptBuffer.flush()\n signalSerializationComplete()\n },\n })\n },\n isDehydrated() {\n return _dehydrated\n },\n isSerializationFinished() {\n return _serializationFinished\n },\n onRenderFinished: (listener) => renderFinishedListeners.push(listener),\n onSerializationFinished: (listener) =>\n serializationFinishedListeners.push(listener),\n setRenderFinished: () => {\n // Wrap in try-catch to ensure scriptBuffer.liftBarrier() is always called\n try {\n renderFinishedListeners.forEach((l) => l())\n } catch (err) {\n console.error('Error in render finished listener:', err)\n } finally {\n // Clear listeners after calling them to prevent memory leaks\n renderFinishedListeners.length = 0\n }\n scriptBuffer.liftBarrier()\n },\n takeBufferedScripts() {\n const scripts = scriptBuffer.takeAll()\n const serverBufferedScript: RouterManagedTag = {\n tag: 'script',\n attrs: {\n nonce: router.options.ssr?.nonce,\n className: '$tsr',\n id: TSR_SCRIPT_BARRIER_ID,\n },\n children: scripts,\n }\n return serverBufferedScript\n },\n liftScriptBarrier() {\n scriptBuffer.liftBarrier()\n },\n takeBufferedHtml() {\n if (!injectedHtmlBuffer) {\n return undefined\n }\n const buffered = injectedHtmlBuffer\n injectedHtmlBuffer = ''\n return buffered\n },\n cleanup() {\n // Guard against multiple cleanup calls\n if (!router.serverSsr) return\n renderFinishedListeners.length = 0\n serializationFinishedListeners.length = 0\n injectedHtmlBuffer = ''\n scriptBuffer.cleanup()\n router.serverSsr = undefined\n },\n }\n}\n\n/**\n * Get the origin for the request.\n *\n * SECURITY: We intentionally do NOT trust the Origin header for determining\n * the router's origin. The Origin header can be spoofed by attackers, which\n * could lead to SSRF-like vulnerabilities where redirects are constructed\n * using a malicious origin (CVE-2024-34351).\n *\n * Instead, we derive the origin from request.url, which is typically set by\n * the server infrastructure (not client-controlled headers).\n *\n * For applications behind proxies that need to trust forwarded headers,\n * use the router's `origin` option to explicitly configure a trusted origin.\n */\nexport function getOrigin(request: Request) {\n try {\n return new URL(request.url).origin\n } catch {}\n return 'http://localhost'\n}\n\n// server and browser can decode/encode characters differently in paths and search params.\n// Server generally strictly follows the WHATWG URL Standard, while browsers may differ for legacy reasons.\n// for example, in paths \"|\" is not encoded on the server but is encoded on chromium (and not on firefox) while \"대\" is encoded on both sides.\n// Another anomaly is that in Node new URLSearchParams and new URL also decode/encode characters differently.\n// new URLSearchParams() encodes \"|\" while new URL() does not, and in this instance\n// chromium treats search params differently than paths, i.e. \"|\" is not encoded in search params.\nexport function getNormalizedURL(url: string | URL, base?: string | URL) {\n // ensure backslashes are encoded correctly in the URL\n if (typeof url === 'string') url = url.replace('\\\\', '%5C')\n\n const rawUrl = new URL(url, base)\n const { path: decodedPathname, handledProtocolRelativeURL } = decodePath(\n rawUrl.pathname,\n )\n const searchParams = new URLSearchParams(rawUrl.search)\n const normalizedHref =\n decodedPathname +\n (searchParams.size > 0 ? '?' : '') +\n searchParams.toString() +\n rawUrl.hash\n\n return {\n url: new URL(normalizedHref, rawUrl.origin),\n handledProtocolRelativeURL,\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ssr-server.js","names":[],"sources":["../../../src/ssr/ssr-server.ts"],"sourcesContent":["import { crossSerializeStream, getCrossReferenceHeader } from 'seroval'\nimport { invariant } from '../invariant'\nimport {\n createInlineCssPlaceholderAsset,\n createInlineCssStyleAsset,\n getStylesheetHref,\n isInlinableStylesheet,\n} from '../manifest'\nimport { decodePath } from '../utils'\nimport { createLRUCache } from '../lru-cache'\nimport { rootRouteId } from '../root'\nimport minifiedTsrBootStrapScript from './tsrScript?script-string'\nimport { GLOBAL_TSR, TSR_SCRIPT_BARRIER_ID } from './constants'\nimport { dehydrateSsrMatchId } from './ssr-match-id'\nimport { defaultSerovalPlugins } from './serializer/seroval-plugins'\nimport { makeSsrSerovalPlugin } from './serializer/transformer'\nimport type { LRUCache } from '../lru-cache'\nimport type { DehydratedMatch, DehydratedRouter } from './types'\nimport type { AnySerializationAdapter } from './serializer/transformer'\nimport type { AnyRouter } from '../router'\nimport type { AnyRouteMatch } from '../Matches'\nimport type { Manifest, RouterManagedTag } from '../manifest'\n\ndeclare module '../router' {\n interface ServerSsr {\n setRenderFinished: () => void\n cleanup: () => void\n }\n interface RouterEvents {\n onInjectedHtml: {\n type: 'onInjectedHtml'\n }\n onSerializationFinished: {\n type: 'onSerializationFinished'\n }\n }\n}\n\nconst SCOPE_ID = 'tsr'\n\nconst TSR_PREFIX = GLOBAL_TSR + '.router='\nconst P_PREFIX = GLOBAL_TSR + '.p(()=>'\nconst P_SUFFIX = ')'\n\nexport function dehydrateMatch(match: AnyRouteMatch): DehydratedMatch {\n const dehydratedMatch: DehydratedMatch = {\n i: dehydrateSsrMatchId(match.id),\n u: match.updatedAt,\n s: match.status,\n }\n\n const properties = [\n ['__beforeLoadContext', 'b'],\n ['loaderData', 'l'],\n ['error', 'e'],\n ['ssr', 'ssr'],\n ] as const\n\n for (const [key, shorthand] of properties) {\n if (match[key] !== undefined) {\n dehydratedMatch[shorthand] = match[key]\n }\n }\n if (match.globalNotFound) {\n dehydratedMatch.g = true\n }\n return dehydratedMatch\n}\n\nconst INITIAL_SCRIPTS = [\n getCrossReferenceHeader(SCOPE_ID),\n minifiedTsrBootStrapScript,\n]\n\nclass ScriptBuffer {\n private router: AnyRouter | undefined\n private _queue: Array<string>\n private _scriptBarrierLifted = false\n private _cleanedUp = false\n private _pendingMicrotask = false\n\n constructor(router: AnyRouter) {\n this.router = router\n // Copy INITIAL_SCRIPTS to avoid mutating the shared array\n this._queue = INITIAL_SCRIPTS.slice()\n }\n\n enqueue(script: string) {\n if (this._cleanedUp) return\n this._queue.push(script)\n // If barrier is lifted, schedule injection (if not already scheduled)\n if (this._scriptBarrierLifted && !this._pendingMicrotask) {\n this._pendingMicrotask = true\n queueMicrotask(() => {\n this._pendingMicrotask = false\n this.injectBufferedScripts()\n })\n }\n }\n\n liftBarrier() {\n if (this._scriptBarrierLifted || this._cleanedUp) return\n this._scriptBarrierLifted = true\n if (this._queue.length > 0 && !this._pendingMicrotask) {\n this._pendingMicrotask = true\n queueMicrotask(() => {\n this._pendingMicrotask = false\n this.injectBufferedScripts()\n })\n }\n }\n\n /**\n * Flushes any pending scripts synchronously.\n * Call this before emitting onSerializationFinished to ensure all scripts are injected.\n *\n * IMPORTANT: Only injects if the barrier has been lifted. Before the barrier is lifted,\n * scripts should remain in the queue so takeBufferedScripts() can retrieve them\n */\n flush() {\n if (!this._scriptBarrierLifted) return\n if (this._cleanedUp) return\n this._pendingMicrotask = false\n const scriptsToInject = this.takeAll()\n if (scriptsToInject && this.router?.serverSsr) {\n this.router.serverSsr.injectScript(scriptsToInject)\n }\n }\n\n takeAll() {\n const bufferedScripts = this._queue\n this._queue = []\n if (bufferedScripts.length === 0) {\n return undefined\n }\n // Optimization: if only one script, avoid join\n if (bufferedScripts.length === 1) {\n return bufferedScripts[0] + ';document.currentScript.remove()'\n }\n // Append cleanup script and join - avoid push() to not mutate then iterate\n return bufferedScripts.join(';') + ';document.currentScript.remove()'\n }\n\n injectBufferedScripts() {\n if (this._cleanedUp) return\n // Early return if queue is empty (avoids unnecessary takeAll() call)\n if (this._queue.length === 0) return\n const scriptsToInject = this.takeAll()\n if (scriptsToInject && this.router?.serverSsr) {\n this.router.serverSsr.injectScript(scriptsToInject)\n }\n }\n\n cleanup() {\n this._cleanedUp = true\n this._queue = []\n this.router = undefined\n }\n}\n\nconst isProd = process.env.NODE_ENV === 'production'\n\ntype FilteredRoutes = Manifest['routes']\n\ntype ManifestLRU = LRUCache<string, FilteredRoutes>\ntype InlineCssLRU = LRUCache<string, string>\n\nconst MANIFEST_CACHE_SIZE = 100\nconst manifestCaches = new WeakMap<Manifest, ManifestLRU>()\nconst inlineCssCaches = new WeakMap<Manifest, InlineCssLRU>()\n\nfunction getManifestCache(manifest: Manifest): ManifestLRU {\n const cache = manifestCaches.get(manifest)\n if (cache) return cache\n const newCache = createLRUCache<string, FilteredRoutes>(MANIFEST_CACHE_SIZE)\n manifestCaches.set(manifest, newCache)\n return newCache\n}\n\nfunction getInlineCssCache(manifest: Manifest): InlineCssLRU {\n const cache = inlineCssCaches.get(manifest)\n if (cache) return cache\n const newCache = createLRUCache<string, string>(MANIFEST_CACHE_SIZE)\n inlineCssCaches.set(manifest, newCache)\n return newCache\n}\n\nfunction getInlineCssHrefsForMatches(\n manifest: Manifest | undefined,\n matches: Array<AnyRouteMatch>,\n) {\n const styles = manifest?.inlineCss?.styles\n if (!styles) return []\n\n const seen = new Set<string>()\n const hrefs: Array<string> = []\n\n for (const match of matches) {\n const assets = manifest?.routes[match.routeId]?.assets ?? []\n for (const asset of assets) {\n const href = getStylesheetHref(asset)\n if (!href || seen.has(href) || styles[href] === undefined) {\n continue\n }\n seen.add(href)\n hrefs.push(href)\n }\n }\n\n return hrefs\n}\n\nfunction getInlineCssForHrefs(manifest: Manifest, hrefs: Array<string>) {\n const styles = manifest.inlineCss?.styles\n if (!styles || hrefs.length === 0) return undefined\n\n const cacheKey = hrefs.join('\\0')\n if (isProd) {\n const cached = getInlineCssCache(manifest).get(cacheKey)\n if (cached !== undefined) return cached\n }\n\n const css = hrefs.map((href) => styles[href]!).join('')\n\n if (isProd) {\n getInlineCssCache(manifest).set(cacheKey, css)\n }\n\n return css\n}\n\nfunction getInlineCssAssetForMatches(\n manifest: Manifest | undefined,\n matches: Array<AnyRouteMatch>,\n) {\n if (!manifest?.inlineCss) return undefined\n\n const hrefs = getInlineCssHrefsForMatches(manifest, matches)\n const css = getInlineCssForHrefs(manifest, hrefs)\n\n return css === undefined ? undefined : createInlineCssStyleAsset(css)\n}\n\nfunction stripInlinedStylesheetAssets(\n manifest: Manifest,\n routes: FilteredRoutes,\n matches: Array<AnyRouteMatch>,\n): FilteredRoutes {\n if (!manifest.inlineCss) {\n return routes\n }\n\n const nextRoutes: FilteredRoutes = {}\n\n for (const [routeId, route] of Object.entries(routes)) {\n const assets = route.assets?.filter(\n (asset) => !isInlinableStylesheet(manifest, asset),\n )\n\n const nextRoute = { ...route }\n if (assets) {\n if (assets.length > 0) {\n nextRoute.assets = assets\n } else {\n delete nextRoute.assets\n }\n }\n nextRoutes[routeId] = nextRoute\n }\n\n if (getInlineCssAssetForMatches(manifest, matches)) {\n const rootRoute = nextRoutes[rootRouteId] ?? {}\n nextRoutes[rootRouteId] = {\n ...rootRoute,\n assets: [createInlineCssPlaceholderAsset(), ...(rootRoute.assets ?? [])],\n }\n }\n\n return nextRoutes\n}\n\nexport function attachRouterServerSsrUtils({\n router,\n manifest,\n getRequestAssets,\n includeUnmatchedRouteAssets = true,\n}: {\n router: AnyRouter\n manifest: Manifest | undefined\n getRequestAssets?: () => Array<RouterManagedTag> | undefined\n includeUnmatchedRouteAssets?: boolean\n}) {\n router.ssr = {\n get manifest() {\n const requestAssets = getRequestAssets?.()\n const inlineCssAsset = getInlineCssAssetForMatches(\n manifest,\n router.stores.matches.get(),\n )\n if (!requestAssets?.length && !inlineCssAsset) return manifest\n // Merge request-scoped assets into root route without mutating cached manifest\n return {\n ...manifest,\n routes: {\n ...manifest?.routes,\n [rootRouteId]: {\n ...manifest?.routes?.[rootRouteId],\n assets: [\n ...(requestAssets ?? []),\n ...(inlineCssAsset ? [inlineCssAsset] : []),\n ...(manifest?.routes?.[rootRouteId]?.assets ?? []),\n ],\n },\n },\n }\n },\n }\n let _dehydrated = false\n let _serializationFinished = false\n const renderFinishedListeners: Array<() => void> = []\n const serializationFinishedListeners: Array<() => void> = []\n const scriptBuffer = new ScriptBuffer(router)\n let injectedHtmlBuffer = ''\n\n router.serverSsr = {\n injectHtml: (html: string) => {\n if (!html) return\n // Buffer the HTML so it can be retrieved via takeBufferedHtml()\n injectedHtmlBuffer += html\n // Emit event to notify subscribers that new HTML is available\n router.emit({\n type: 'onInjectedHtml',\n })\n },\n injectScript: (script: string) => {\n if (!script) return\n const html = `<script${router.options.ssr?.nonce ? ` nonce='${router.options.ssr.nonce}'` : ''}>${script}</script>`\n router.serverSsr!.injectHtml(html)\n },\n dehydrate: async (opts?: { requestAssets?: Array<RouterManagedTag> }) => {\n if (_dehydrated) {\n if (process.env.NODE_ENV !== 'production') {\n throw new Error('Invariant failed: router is already dehydrated!')\n }\n\n invariant()\n }\n let matchesToDehydrate = router.stores.matches.get()\n if (router.isShell()) {\n // In SPA mode we only want to dehydrate the root match\n matchesToDehydrate = matchesToDehydrate.slice(0, 1)\n }\n const matches = matchesToDehydrate.map(dehydrateMatch)\n\n let manifestToDehydrate: Manifest | undefined = undefined\n // For currently matched routes, send full manifest (preloads + assets).\n // For unmatched routes, include assets only when includeUnmatchedRouteAssets\n // is true; otherwise omit them entirely. Preloads for unmatched routes are\n // still excluded because they are handled via dynamic imports.\n if (manifest) {\n // Prod-only caching; in dev manifests may be replaced/updated (HMR)\n const currentRouteIdsList = matchesToDehydrate.map((m) => m.routeId)\n const manifestCacheKey = `${currentRouteIdsList.join('\\0')}\\0includeUnmatchedRouteAssets=${includeUnmatchedRouteAssets}`\n\n let filteredRoutes: FilteredRoutes | undefined\n\n if (isProd) {\n filteredRoutes = getManifestCache(manifest).get(manifestCacheKey)\n }\n\n if (!filteredRoutes) {\n const currentRouteIds = new Set(currentRouteIdsList)\n const nextFilteredRoutes: FilteredRoutes = {}\n\n for (const routeId in manifest.routes) {\n const routeManifest = manifest.routes[routeId]!\n if (currentRouteIds.has(routeId)) {\n nextFilteredRoutes[routeId] = routeManifest\n } else if (\n includeUnmatchedRouteAssets &&\n routeManifest.assets &&\n routeManifest.assets.length > 0\n ) {\n nextFilteredRoutes[routeId] = {\n assets: routeManifest.assets,\n }\n }\n }\n\n filteredRoutes = stripInlinedStylesheetAssets(\n manifest,\n nextFilteredRoutes,\n matchesToDehydrate,\n )\n\n if (isProd) {\n getManifestCache(manifest).set(manifestCacheKey, filteredRoutes)\n }\n }\n\n manifestToDehydrate = {\n routes: { ...filteredRoutes },\n }\n\n // Merge request-scoped assets into root route (without mutating cached manifest)\n if (opts?.requestAssets?.length) {\n const existingRoot = manifestToDehydrate.routes[rootRouteId]\n manifestToDehydrate.routes[rootRouteId] = {\n ...existingRoot,\n assets: [...opts.requestAssets, ...(existingRoot?.assets ?? [])],\n }\n }\n }\n const dehydratedRouter: DehydratedRouter = {\n manifest: manifestToDehydrate,\n matches,\n }\n const lastMatchId = matchesToDehydrate[matchesToDehydrate.length - 1]?.id\n if (lastMatchId) {\n dehydratedRouter.lastMatchId = dehydrateSsrMatchId(lastMatchId)\n }\n const dehydratedData = await router.options.dehydrate?.()\n if (dehydratedData) {\n dehydratedRouter.dehydratedData = dehydratedData\n }\n _dehydrated = true\n\n const trackPlugins = { didRun: false }\n const serializationAdapters = router.options.serializationAdapters as\n | Array<AnySerializationAdapter>\n | undefined\n const plugins = serializationAdapters\n ? serializationAdapters\n .map((t) => makeSsrSerovalPlugin(t, trackPlugins))\n .concat(defaultSerovalPlugins)\n : defaultSerovalPlugins\n\n const signalSerializationComplete = () => {\n _serializationFinished = true\n try {\n serializationFinishedListeners.forEach((l) => l())\n router.emit({ type: 'onSerializationFinished' })\n } catch (err) {\n console.error('Serialization listener error:', err)\n } finally {\n serializationFinishedListeners.length = 0\n renderFinishedListeners.length = 0\n }\n }\n\n crossSerializeStream(dehydratedRouter, {\n refs: new Map(),\n plugins,\n onSerialize: (data, initial) => {\n let serialized = initial ? TSR_PREFIX + data : data\n if (trackPlugins.didRun) {\n serialized = P_PREFIX + serialized + P_SUFFIX\n }\n scriptBuffer.enqueue(serialized)\n },\n onError: (err: unknown) => {\n console.error('Serialization error:', err)\n if (err && (err as any).stack) {\n console.error((err as any).stack)\n }\n signalSerializationComplete()\n },\n scopeId: SCOPE_ID,\n onDone: () => {\n scriptBuffer.enqueue(GLOBAL_TSR + '.e()')\n // Flush all pending scripts synchronously before signaling completion\n // This ensures all scripts are injected before onSerializationFinished is emitted\n scriptBuffer.flush()\n signalSerializationComplete()\n },\n })\n },\n isDehydrated() {\n return _dehydrated\n },\n isSerializationFinished() {\n return _serializationFinished\n },\n onRenderFinished: (listener) => renderFinishedListeners.push(listener),\n onSerializationFinished: (listener) =>\n serializationFinishedListeners.push(listener),\n setRenderFinished: () => {\n // Wrap in try-catch to ensure scriptBuffer.liftBarrier() is always called\n try {\n renderFinishedListeners.forEach((l) => l())\n } catch (err) {\n console.error('Error in render finished listener:', err)\n } finally {\n // Clear listeners after calling them to prevent memory leaks\n renderFinishedListeners.length = 0\n }\n scriptBuffer.liftBarrier()\n },\n takeBufferedScripts() {\n const scripts = scriptBuffer.takeAll()\n const serverBufferedScript: RouterManagedTag = {\n tag: 'script',\n attrs: {\n nonce: router.options.ssr?.nonce,\n className: '$tsr',\n id: TSR_SCRIPT_BARRIER_ID,\n },\n children: scripts,\n }\n return serverBufferedScript\n },\n liftScriptBarrier() {\n scriptBuffer.liftBarrier()\n },\n takeBufferedHtml() {\n if (!injectedHtmlBuffer) {\n return undefined\n }\n const buffered = injectedHtmlBuffer\n injectedHtmlBuffer = ''\n return buffered\n },\n cleanup() {\n // Guard against multiple cleanup calls\n if (!router.serverSsr) return\n renderFinishedListeners.length = 0\n serializationFinishedListeners.length = 0\n injectedHtmlBuffer = ''\n scriptBuffer.cleanup()\n router.serverSsr = undefined\n },\n }\n}\n\n/**\n * Get the origin for the request.\n *\n * SECURITY: We intentionally do NOT trust the Origin header for determining\n * the router's origin. The Origin header can be spoofed by attackers, which\n * could lead to SSRF-like vulnerabilities where redirects are constructed\n * using a malicious origin (CVE-2024-34351).\n *\n * Instead, we derive the origin from request.url, which is typically set by\n * the server infrastructure (not client-controlled headers).\n *\n * For applications behind proxies that need to trust forwarded headers,\n * use the router's `origin` option to explicitly configure a trusted origin.\n */\nexport function getOrigin(request: Request) {\n try {\n return new URL(request.url).origin\n } catch {}\n return 'http://localhost'\n}\n\n// server and browser can decode/encode characters differently in paths and search params.\n// Server generally strictly follows the WHATWG URL Standard, while browsers may differ for legacy reasons.\n// for example, in paths \"|\" is not encoded on the server but is encoded on chromium (and not on firefox) while \"대\" is encoded on both sides.\n// Another anomaly is that in Node new URLSearchParams and new URL also decode/encode characters differently.\n// new URLSearchParams() encodes \"|\" while new URL() does not, and in this instance\n// chromium treats search params differently than paths, i.e. \"|\" is not encoded in search params.\nexport function getNormalizedURL(url: string | URL, base?: string | URL) {\n // ensure backslashes are encoded correctly in the URL\n if (typeof url === 'string') url = url.replace('\\\\', '%5C')\n\n const rawUrl = new URL(url, base)\n const { path: decodedPathname, handledProtocolRelativeURL } = decodePath(\n rawUrl.pathname,\n )\n const searchParams = new URLSearchParams(rawUrl.search)\n const normalizedHref =\n decodedPathname +\n (searchParams.size > 0 ? '?' : '') +\n searchParams.toString() +\n rawUrl.hash\n\n return {\n url: new URL(normalizedHref, rawUrl.origin),\n handledProtocolRelativeURL,\n }\n}\n"],"mappings":"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"}
@@ -8,12 +8,12 @@ function transformReadableStreamWithRouter(router, routerStream) {
8
8
  function transformPipeableStreamWithRouter(router, routerStream) {
9
9
  return Readable.fromWeb(transformStreamWithRouter(router, Readable.toWeb(routerStream)));
10
10
  }
11
- var BODY_END_TAG = "</body>";
12
- var HTML_END_TAG = "</html>";
13
- var MIN_CLOSING_TAG_LENGTH = 4;
14
- var DEFAULT_SERIALIZATION_TIMEOUT_MS = 6e4;
15
- var DEFAULT_LIFETIME_TIMEOUT_MS = 6e4;
16
- var textEncoder = new TextEncoder();
11
+ const BODY_END_TAG = "</body>";
12
+ const HTML_END_TAG = "</html>";
13
+ const MIN_CLOSING_TAG_LENGTH = 4;
14
+ const DEFAULT_SERIALIZATION_TIMEOUT_MS = 6e4;
15
+ const DEFAULT_LIFETIME_TIMEOUT_MS = 6e4;
16
+ const textEncoder = new TextEncoder();
17
17
  /**
18
18
  * Finds the position just after the last valid HTML closing tag in the string.
19
19
  *