@tanstack/router-core 1.171.15 → 1.171.16-pre.0

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 (134) hide show
  1. package/dist/cjs/Matches.cjs.map +1 -1
  2. package/dist/cjs/Matches.d.cts +3 -12
  3. package/dist/cjs/await-signal.cjs +21 -0
  4. package/dist/cjs/await-signal.cjs.map +1 -0
  5. package/dist/cjs/await-signal.d.cts +1 -0
  6. package/dist/cjs/index.cjs +3 -1
  7. package/dist/cjs/index.d.cts +3 -2
  8. package/dist/cjs/isServer/client.cjs +2 -0
  9. package/dist/cjs/isServer/client.cjs.map +1 -1
  10. package/dist/cjs/isServer/client.d.cts +1 -0
  11. package/dist/cjs/isServer/development.cjs +2 -0
  12. package/dist/cjs/isServer/development.cjs.map +1 -1
  13. package/dist/cjs/isServer/development.d.cts +1 -0
  14. package/dist/cjs/isServer/server.cjs +2 -0
  15. package/dist/cjs/isServer/server.cjs.map +1 -1
  16. package/dist/cjs/isServer/server.d.cts +1 -0
  17. package/dist/cjs/load-client.cjs +1470 -0
  18. package/dist/cjs/load-client.cjs.map +1 -0
  19. package/dist/cjs/load-client.d.cts +147 -0
  20. package/dist/cjs/load-server.cjs +565 -0
  21. package/dist/cjs/load-server.cjs.map +1 -0
  22. package/dist/cjs/load-server.d.cts +16 -0
  23. package/dist/cjs/redirect.cjs.map +1 -1
  24. package/dist/cjs/redirect.d.cts +0 -1
  25. package/dist/cjs/route.cjs.map +1 -1
  26. package/dist/cjs/router.cjs +187 -442
  27. package/dist/cjs/router.cjs.map +1 -1
  28. package/dist/cjs/router.d.cts +61 -51
  29. package/dist/cjs/ssr/client.cjs +2 -2
  30. package/dist/cjs/ssr/createRequestHandler.cjs +60 -9
  31. package/dist/cjs/ssr/createRequestHandler.cjs.map +1 -1
  32. package/dist/cjs/ssr/createRequestHandler.d.cts +1 -0
  33. package/dist/cjs/ssr/handlerCallback.cjs +49 -4
  34. package/dist/cjs/ssr/handlerCallback.cjs.map +1 -1
  35. package/dist/cjs/ssr/handlerCallback.d.cts +3 -0
  36. package/dist/cjs/ssr/server.cjs +4 -0
  37. package/dist/cjs/ssr/server.d.cts +2 -2
  38. package/dist/cjs/ssr/ssr-client.d.cts +1 -10
  39. package/dist/cjs/ssr/ssr-match-id.cjs +2 -2
  40. package/dist/cjs/ssr/ssr-match-id.cjs.map +1 -1
  41. package/dist/cjs/ssr/ssr-server.cjs +6 -6
  42. package/dist/cjs/ssr/ssr-server.cjs.map +1 -1
  43. package/dist/cjs/ssr/transformStreamWithRouter.cjs +27 -1
  44. package/dist/cjs/ssr/transformStreamWithRouter.cjs.map +1 -1
  45. package/dist/cjs/ssr/transformStreamWithRouter.d.cts +2 -0
  46. package/dist/cjs/ssr/types.d.cts +0 -1
  47. package/dist/cjs/stores.cjs +30 -104
  48. package/dist/cjs/stores.cjs.map +1 -1
  49. package/dist/cjs/stores.d.cts +7 -32
  50. package/dist/cjs/utils.cjs +0 -4
  51. package/dist/cjs/utils.cjs.map +1 -1
  52. package/dist/esm/Matches.d.ts +3 -12
  53. package/dist/esm/Matches.js.map +1 -1
  54. package/dist/esm/await-signal.d.ts +1 -0
  55. package/dist/esm/await-signal.js +21 -0
  56. package/dist/esm/await-signal.js.map +1 -0
  57. package/dist/esm/index.d.ts +3 -2
  58. package/dist/esm/index.js +3 -2
  59. package/dist/esm/isServer/client.d.ts +1 -0
  60. package/dist/esm/isServer/client.js +2 -1
  61. package/dist/esm/isServer/client.js.map +1 -1
  62. package/dist/esm/isServer/development.d.ts +1 -0
  63. package/dist/esm/isServer/development.js +2 -1
  64. package/dist/esm/isServer/development.js.map +1 -1
  65. package/dist/esm/isServer/server.d.ts +1 -0
  66. package/dist/esm/isServer/server.js +2 -1
  67. package/dist/esm/isServer/server.js.map +1 -1
  68. package/dist/esm/load-client.d.ts +147 -0
  69. package/dist/esm/load-client.js +1462 -0
  70. package/dist/esm/load-client.js.map +1 -0
  71. package/dist/esm/load-server.d.ts +16 -0
  72. package/dist/esm/load-server.js +565 -0
  73. package/dist/esm/load-server.js.map +1 -0
  74. package/dist/esm/redirect.d.ts +0 -1
  75. package/dist/esm/redirect.js.map +1 -1
  76. package/dist/esm/route.js.map +1 -1
  77. package/dist/esm/router.d.ts +61 -51
  78. package/dist/esm/router.js +188 -444
  79. package/dist/esm/router.js.map +1 -1
  80. package/dist/esm/ssr/client.js +1 -1
  81. package/dist/esm/ssr/createRequestHandler.d.ts +1 -0
  82. package/dist/esm/ssr/createRequestHandler.js +61 -11
  83. package/dist/esm/ssr/createRequestHandler.js.map +1 -1
  84. package/dist/esm/ssr/handlerCallback.d.ts +3 -0
  85. package/dist/esm/ssr/handlerCallback.js +47 -5
  86. package/dist/esm/ssr/handlerCallback.js.map +1 -1
  87. package/dist/esm/ssr/server.d.ts +2 -2
  88. package/dist/esm/ssr/server.js +3 -3
  89. package/dist/esm/ssr/ssr-client.d.ts +1 -10
  90. package/dist/esm/ssr/ssr-match-id.js +2 -2
  91. package/dist/esm/ssr/ssr-match-id.js.map +1 -1
  92. package/dist/esm/ssr/ssr-server.js +6 -6
  93. package/dist/esm/ssr/ssr-server.js.map +1 -1
  94. package/dist/esm/ssr/transformStreamWithRouter.d.ts +2 -0
  95. package/dist/esm/ssr/transformStreamWithRouter.js +27 -1
  96. package/dist/esm/ssr/transformStreamWithRouter.js.map +1 -1
  97. package/dist/esm/ssr/types.d.ts +0 -1
  98. package/dist/esm/stores.d.ts +7 -32
  99. package/dist/esm/stores.js +30 -104
  100. package/dist/esm/stores.js.map +1 -1
  101. package/dist/esm/utils.js +1 -4
  102. package/dist/esm/utils.js.map +1 -1
  103. package/package.json +3 -3
  104. package/src/Matches.ts +6 -21
  105. package/src/await-signal.ts +27 -0
  106. package/src/index.ts +1 -3
  107. package/src/isServer/client.ts +1 -0
  108. package/src/isServer/development.ts +1 -0
  109. package/src/isServer/server.ts +1 -0
  110. package/src/load-client.ts +2701 -0
  111. package/src/load-server.ts +849 -0
  112. package/src/redirect.ts +0 -1
  113. package/src/route.ts +2 -11
  114. package/src/router.ts +379 -769
  115. package/src/ssr/createRequestHandler.ts +109 -16
  116. package/src/ssr/handlerCallback.ts +76 -10
  117. package/src/ssr/server.ts +4 -1
  118. package/src/ssr/ssr-client.ts +1 -310
  119. package/src/ssr/ssr-match-id.ts +11 -2
  120. package/src/ssr/ssr-server.ts +9 -8
  121. package/src/ssr/transformStreamWithRouter.ts +37 -1
  122. package/src/ssr/types.ts +0 -1
  123. package/src/stores.ts +46 -208
  124. package/dist/cjs/load-matches.cjs +0 -658
  125. package/dist/cjs/load-matches.cjs.map +0 -1
  126. package/dist/cjs/load-matches.d.cts +0 -18
  127. package/dist/cjs/ssr/ssr-client.cjs +0 -183
  128. package/dist/cjs/ssr/ssr-client.cjs.map +0 -1
  129. package/dist/esm/load-matches.d.ts +0 -18
  130. package/dist/esm/load-matches.js +0 -656
  131. package/dist/esm/load-matches.js.map +0 -1
  132. package/dist/esm/ssr/ssr-client.js +0 -183
  133. package/dist/esm/ssr/ssr-client.js.map +0 -1
  134. package/src/load-matches.ts +0 -1278
@@ -1,4 +1,4 @@
1
+ import { hydrate } from "../load-client.js";
1
2
  import { mergeHeaders } from "./headers.js";
2
3
  import { json } from "./json.js";
3
- import { hydrate } from "./ssr-client.js";
4
4
  export { hydrate, json, mergeHeaders };
@@ -2,6 +2,7 @@ import { HandlerCallback } from './handlerCallback.js';
2
2
  import { AnyRouter } from '../router.js';
3
3
  import { ServerManifest } from '../manifest.js';
4
4
  export type RequestHandler<TRouter extends AnyRouter> = (cb: HandlerCallback<TRouter>) => Promise<Response>;
5
+ export declare function waitForRequest<T>(value: T | PromiseLike<T>, signal: AbortSignal, onLate?: (value: T) => void): Promise<T>;
5
6
  export declare function createRequestHandler<TRouter extends AnyRouter>({ createRouter, request, getRouterManifest, }: {
6
7
  createRouter: () => TRouter;
7
8
  request: Request;
@@ -1,16 +1,59 @@
1
+ import { _getRenderedMatches } from "../load-client.js";
1
2
  import { mergeHeaders } from "./headers.js";
2
3
  import { attachRouterServerSsrUtils, getNormalizedURL, getOrigin } from "./ssr-server.js";
3
- import { normalizeSsrResponse } from "./handlerCallback.js";
4
+ import { bindSsrResponseToRequest, disposeSsrResponseDetached } from "./handlerCallback.js";
4
5
  import { createMemoryHistory } from "@tanstack/history";
5
6
  //#region src/ssr/createRequestHandler.ts
7
+ const requestWaiters = /* @__PURE__ */ new WeakMap();
8
+ function removeRequestWaiter(waiters, index, reject) {
9
+ if (waiters[index] !== reject) return;
10
+ if (index !== waiters.length - 1) {
11
+ waiters[index] = void 0;
12
+ return;
13
+ }
14
+ waiters.pop();
15
+ while (waiters.length && waiters[waiters.length - 1] === void 0) waiters.pop();
16
+ }
17
+ function waitForRequest(value, signal, onLate) {
18
+ const promise = Promise.resolve(value);
19
+ if (signal.aborted) {
20
+ promise.then(onLate, () => {});
21
+ return Promise.reject(signal.reason);
22
+ }
23
+ return new Promise((resolve, reject) => {
24
+ let waiters = requestWaiters.get(signal);
25
+ let index;
26
+ if (waiters) index = waiters.push(reject) - 1;
27
+ else {
28
+ const newWaiters = [reject];
29
+ waiters = newWaiters;
30
+ index = 0;
31
+ requestWaiters.set(signal, newWaiters);
32
+ signal.addEventListener("abort", () => {
33
+ requestWaiters.delete(signal);
34
+ for (const rejectWaiter of newWaiters) rejectWaiter?.(signal.reason);
35
+ newWaiters.length = 0;
36
+ }, { once: true });
37
+ }
38
+ promise.then((result) => {
39
+ removeRequestWaiter(waiters, index, reject);
40
+ if (signal.aborted) onLate?.(result);
41
+ else resolve(result);
42
+ }, (error) => {
43
+ removeRequestWaiter(waiters, index, reject);
44
+ reject(error);
45
+ });
46
+ });
47
+ }
6
48
  function createRequestHandler({ createRouter, request, getRouterManifest }) {
7
49
  return async (cb) => {
50
+ request.signal.throwIfAborted();
8
51
  const router = createRouter();
9
52
  let responseOwnsCleanup = false;
10
53
  try {
11
54
  attachRouterServerSsrUtils({
12
55
  router,
13
- manifest: await getRouterManifest?.()
56
+ manifest: await waitForRequest(getRouterManifest?.(), request.signal)
14
57
  });
15
58
  const { url } = getNormalizedURL(request.url, "http://localhost");
16
59
  const origin = getOrigin(request);
@@ -19,13 +62,22 @@ function createRequestHandler({ createRouter, request, getRouterManifest }) {
19
62
  history,
20
63
  origin: router.options.origin ?? origin
21
64
  });
22
- await router.load();
23
- await router.serverSsr?.dehydrate();
24
- const ssrResponse = normalizeSsrResponse(await cb({
65
+ await router.load({ _signal: request.signal });
66
+ request.signal.throwIfAborted();
67
+ const result = router._serverResult;
68
+ if (result?.type === "redirect") return result.redirect;
69
+ await waitForRequest(router.serverSsr?.dehydrate(), request.signal);
70
+ request.signal.throwIfAborted();
71
+ const responseHeaders = getRequestHeaders({ router });
72
+ request.signal.throwIfAborted();
73
+ const ssrResponse = bindSsrResponseToRequest(router, await waitForRequest(cb({
25
74
  request,
26
75
  router,
27
- responseHeaders: getRequestHeaders({ router })
28
- }));
76
+ responseHeaders
77
+ }), request.signal, (late) => {
78
+ disposeSsrResponseDetached(late, request.signal.reason);
79
+ }), request.signal);
80
+ request.signal.throwIfAborted();
29
81
  responseOwnsCleanup = ssrResponse.serverSsrCleanup === "stream";
30
82
  return ssrResponse.response;
31
83
  } finally {
@@ -35,12 +87,10 @@ function createRequestHandler({ createRouter, request, getRouterManifest }) {
35
87
  }
36
88
  function getRequestHeaders(opts) {
37
89
  const matchHeaders = [];
38
- for (const match of opts.router.stores.matches.get()) matchHeaders.push(match.headers);
39
- const redirect = opts.router.stores.redirect.get();
40
- if (redirect) matchHeaders.push(redirect.headers);
90
+ for (const match of _getRenderedMatches(opts.router.stores.matches.get())) matchHeaders.push(match.headers);
41
91
  return mergeHeaders({ "Content-Type": "text/html; charset=UTF-8" }, ...matchHeaders);
42
92
  }
43
93
  //#endregion
44
- export { createRequestHandler };
94
+ export { createRequestHandler, waitForRequest };
45
95
 
46
96
  //# sourceMappingURL=createRequestHandler.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"createRequestHandler.js","names":[],"sources":["../../../src/ssr/createRequestHandler.ts"],"sourcesContent":["import { createMemoryHistory } from '@tanstack/history'\nimport { mergeHeaders } from './headers'\nimport {\n attachRouterServerSsrUtils,\n getNormalizedURL,\n getOrigin,\n} from './ssr-server'\nimport { normalizeSsrResponse } from './handlerCallback'\nimport type { HandlerCallback } from './handlerCallback'\nimport type { AnyHeaders } from './headers'\nimport type { AnyRouter } from '../router'\nimport type { ServerManifest } from '../manifest'\n\nexport type RequestHandler<TRouter extends AnyRouter> = (\n cb: HandlerCallback<TRouter>,\n) => Promise<Response>\n\nexport function createRequestHandler<TRouter extends AnyRouter>({\n createRouter,\n request,\n getRouterManifest,\n}: {\n createRouter: () => TRouter\n request: Request\n getRouterManifest?: () => ServerManifest | Promise<ServerManifest>\n}): RequestHandler<TRouter> {\n return async (cb) => {\n const router = createRouter()\n let responseOwnsCleanup = false\n\n try {\n attachRouterServerSsrUtils({\n router,\n manifest: await getRouterManifest?.(),\n })\n\n // normalizing and sanitizing the pathname here for server, so we always deal with the same format during SSR.\n const { url } = getNormalizedURL(request.url, 'http://localhost')\n const origin = getOrigin(request)\n const href = url.href.replace(url.origin, '')\n\n // Create a history for the router\n const history = createMemoryHistory({\n initialEntries: [href],\n })\n\n // Update the router with the history and context\n router.update({\n history,\n origin: router.options.origin ?? origin,\n })\n\n await router.load()\n\n await router.serverSsr?.dehydrate()\n\n const responseHeaders = getRequestHeaders({\n router,\n })\n\n const response = await cb({\n request,\n router,\n responseHeaders,\n })\n const ssrResponse = normalizeSsrResponse(response)\n responseOwnsCleanup = ssrResponse.serverSsrCleanup === 'stream'\n return ssrResponse.response\n } finally {\n if (!responseOwnsCleanup) {\n // Clean up router SSR state if the callback won't handle it\n // (e.g., if an error occurred before the callback was invoked).\n // Transformed streaming response bodies clean up when consumed/cancelled.\n router.serverSsr?.cleanup()\n }\n }\n }\n}\n\nfunction getRequestHeaders(opts: { router: AnyRouter }): Headers {\n const matchHeaders: Array<AnyHeaders> = []\n for (const match of opts.router.stores.matches.get()) {\n matchHeaders.push(match.headers)\n }\n\n // Handle Redirects\n const redirect = opts.router.stores.redirect.get()\n if (redirect) {\n matchHeaders.push(redirect.headers)\n }\n\n return mergeHeaders(\n {\n 'Content-Type': 'text/html; charset=UTF-8',\n },\n ...matchHeaders,\n )\n}\n"],"mappings":";;;;;AAiBA,SAAgB,qBAAgD,EAC9D,cACA,SACA,qBAK0B;CAC1B,OAAO,OAAO,OAAO;EACnB,MAAM,SAAS,aAAa;EAC5B,IAAI,sBAAsB;EAE1B,IAAI;GACF,2BAA2B;IACzB;IACA,UAAU,MAAM,oBAAoB;GACtC,CAAC;GAGD,MAAM,EAAE,QAAQ,iBAAiB,QAAQ,KAAK,kBAAkB;GAChE,MAAM,SAAS,UAAU,OAAO;GAIhC,MAAM,UAAU,oBAAoB,EAClC,gBAAgB,CAJL,IAAI,KAAK,QAAQ,IAAI,QAAQ,EAIvB,CAAI,EACvB,CAAC;GAGD,OAAO,OAAO;IACZ;IACA,QAAQ,OAAO,QAAQ,UAAU;GACnC,CAAC;GAED,MAAM,OAAO,KAAK;GAElB,MAAM,OAAO,WAAW,UAAU;GAWlC,MAAM,cAAc,qBAAqB,MALlB,GAAG;IACxB;IACA;IACA,iBAPsB,kBAAkB,EACxC,OACF,CAKE;GACF,CAAC,CACgD;GACjD,sBAAsB,YAAY,qBAAqB;GACvD,OAAO,YAAY;EACrB,UAAU;GACR,IAAI,CAAC,qBAIH,OAAO,WAAW,QAAQ;EAE9B;CACF;AACF;AAEA,SAAS,kBAAkB,MAAsC;CAC/D,MAAM,eAAkC,CAAC;CACzC,KAAK,MAAM,SAAS,KAAK,OAAO,OAAO,QAAQ,IAAI,GACjD,aAAa,KAAK,MAAM,OAAO;CAIjC,MAAM,WAAW,KAAK,OAAO,OAAO,SAAS,IAAI;CACjD,IAAI,UACF,aAAa,KAAK,SAAS,OAAO;CAGpC,OAAO,aACL,EACE,gBAAgB,2BAClB,GACA,GAAG,YACL;AACF"}
1
+ {"version":3,"file":"createRequestHandler.js","names":[],"sources":["../../../src/ssr/createRequestHandler.ts"],"sourcesContent":["import { createMemoryHistory } from '@tanstack/history'\nimport { _getRenderedMatches } from '../load-client'\nimport { mergeHeaders } from './headers'\nimport {\n attachRouterServerSsrUtils,\n getNormalizedURL,\n getOrigin,\n} from './ssr-server'\nimport {\n bindSsrResponseToRequest,\n disposeSsrResponseDetached,\n} from './handlerCallback'\nimport type { HandlerCallback } from './handlerCallback'\nimport type { AnyHeaders } from './headers'\nimport type { AnyRouter } from '../router'\nimport type { ServerManifest } from '../manifest'\n\nexport type RequestHandler<TRouter extends AnyRouter> = (\n cb: HandlerCallback<TRouter>,\n) => Promise<Response>\n\ntype RequestWaiter = ((reason: unknown) => void) | undefined\n\nconst requestWaiters = new WeakMap<AbortSignal, Array<RequestWaiter>>()\n\nfunction removeRequestWaiter(\n waiters: Array<RequestWaiter>,\n index: number,\n reject: (reason: unknown) => void,\n) {\n if (waiters[index] !== reject) {\n return\n }\n if (index !== waiters.length - 1) {\n waiters[index] = undefined\n return\n }\n\n waiters.pop()\n while (waiters.length && waiters[waiters.length - 1] === undefined) {\n waiters.pop()\n }\n}\n\nexport function waitForRequest<T>(\n value: T | PromiseLike<T>,\n signal: AbortSignal,\n onLate?: (value: T) => void,\n): Promise<T> {\n const promise = Promise.resolve(value)\n if (signal.aborted) {\n void promise.then(onLate, () => {})\n return Promise.reject(signal.reason)\n }\n\n return new Promise<T>((resolve, reject) => {\n let waiters = requestWaiters.get(signal)\n let index: number\n if (waiters) {\n index = waiters.push(reject) - 1\n } else {\n const newWaiters: Array<RequestWaiter> = [reject]\n waiters = newWaiters\n index = 0\n requestWaiters.set(signal, newWaiters)\n signal.addEventListener(\n 'abort',\n () => {\n requestWaiters.delete(signal)\n for (const rejectWaiter of newWaiters) {\n rejectWaiter?.(signal.reason)\n }\n newWaiters.length = 0\n },\n { once: true },\n )\n }\n void promise.then(\n (result) => {\n removeRequestWaiter(waiters, index, reject)\n if (signal.aborted) {\n onLate?.(result)\n } else {\n resolve(result)\n }\n },\n (error) => {\n removeRequestWaiter(waiters, index, reject)\n reject(error)\n },\n )\n })\n}\n\nexport function createRequestHandler<TRouter extends AnyRouter>({\n createRouter,\n request,\n getRouterManifest,\n}: {\n createRouter: () => TRouter\n request: Request\n getRouterManifest?: () => ServerManifest | Promise<ServerManifest>\n}): RequestHandler<TRouter> {\n return async (cb) => {\n request.signal.throwIfAborted()\n const router = createRouter()\n let responseOwnsCleanup = false\n\n try {\n attachRouterServerSsrUtils({\n router,\n manifest: await waitForRequest(getRouterManifest?.(), request.signal),\n })\n\n // normalizing and sanitizing the pathname here for server, so we always deal with the same format during SSR.\n const { url } = getNormalizedURL(request.url, 'http://localhost')\n const origin = getOrigin(request)\n const href = url.href.replace(url.origin, '')\n\n // Create a history for the router\n const history = createMemoryHistory({\n initialEntries: [href],\n })\n\n // Update the router with the history and context\n router.update({\n history,\n origin: router.options.origin ?? origin,\n })\n\n await router.load({\n _signal: request.signal,\n })\n request.signal.throwIfAborted()\n\n const result = router._serverResult\n if (result?.type === 'redirect') {\n return result.redirect\n }\n\n await waitForRequest(router.serverSsr?.dehydrate(), request.signal)\n request.signal.throwIfAborted()\n\n const responseHeaders = getRequestHeaders({\n router,\n })\n\n request.signal.throwIfAborted()\n const response = await waitForRequest(\n cb({\n request,\n router,\n responseHeaders,\n }),\n request.signal,\n (late) => {\n disposeSsrResponseDetached(late, request.signal.reason)\n },\n )\n const ssrResponse = bindSsrResponseToRequest(\n router,\n response,\n request.signal,\n )\n request.signal.throwIfAborted()\n responseOwnsCleanup = ssrResponse.serverSsrCleanup === 'stream'\n return ssrResponse.response\n } finally {\n if (!responseOwnsCleanup) {\n // Clean up router SSR state if the callback won't handle it\n // (e.g., if an error occurred before the callback was invoked).\n // Transformed streaming response bodies clean up when consumed/cancelled.\n router.serverSsr?.cleanup()\n }\n }\n }\n}\n\nfunction getRequestHeaders(opts: { router: AnyRouter }): Headers {\n const matchHeaders: Array<AnyHeaders> = []\n for (const match of _getRenderedMatches(opts.router.stores.matches.get())) {\n matchHeaders.push(match.headers)\n }\n\n return mergeHeaders(\n {\n 'Content-Type': 'text/html; charset=UTF-8',\n },\n ...matchHeaders,\n )\n}\n"],"mappings":"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"}
@@ -10,7 +10,10 @@ export type SsrResponse = {
10
10
  export type HandlerCallbackResult = Response | SsrResponse;
11
11
  export declare function isSsrResponse(value: unknown): value is SsrResponse;
12
12
  export declare function normalizeSsrResponse(result: HandlerCallbackResult): SsrResponse;
13
+ export declare function disposeSsrResponse(response: SsrResponse, reason?: unknown): Promise<void>;
14
+ export declare function disposeSsrResponseDetached(result: HandlerCallbackResult, reason?: unknown, onError?: (error: unknown) => void): void;
13
15
  export declare function createSsrStreamResponse<TRouter extends AnyRouter>(router: TRouter, response: Response): SsrResponse;
16
+ export declare function bindSsrResponseToRequest(router: AnyRouter | undefined, result: HandlerCallbackResult, signal: AbortSignal): SsrResponse;
14
17
  export declare function replaceSsrResponse(result: HandlerCallbackResult, response: Response, reason?: unknown): Promise<SsrResponse>;
15
18
  export declare function stripSsrResponseBody(result: HandlerCallbackResult, reason?: unknown): Promise<SsrResponse>;
16
19
  export interface HandlerCallback<TRouter extends AnyRouter> {
@@ -8,6 +8,26 @@ function normalizeSsrResponse(result) {
8
8
  serverSsrCleanup: "none"
9
9
  };
10
10
  }
11
+ function disposeSsrResponse(response, reason) {
12
+ if (response.serverSsrCleanup !== "stream") return Promise.resolve();
13
+ try {
14
+ return Promise.resolve(response.dispose(reason));
15
+ } catch (error) {
16
+ return Promise.reject(error);
17
+ }
18
+ }
19
+ function disposeSsrResponseDetached(result, reason, onError = console.error) {
20
+ const ssrResponse = normalizeSsrResponse(result);
21
+ if (ssrResponse.serverSsrCleanup === "stream") {
22
+ disposeSsrResponse(ssrResponse, reason).catch(onError);
23
+ return;
24
+ }
25
+ if (ssrResponse.response.body) try {
26
+ ssrResponse.response.body.cancel(reason).catch(onError);
27
+ } catch (error) {
28
+ onError(error);
29
+ }
30
+ }
11
31
  function createSsrStreamResponse(router, response) {
12
32
  if (!response.body) throw new Error("Invariant failed: SSR stream response requires a body");
13
33
  let disposed = false;
@@ -17,16 +37,38 @@ function createSsrStreamResponse(router, response) {
17
37
  async dispose(reason) {
18
38
  if (disposed) return;
19
39
  disposed = true;
40
+ router.serverSsr?.cleanup();
20
41
  try {
21
42
  await response.body.cancel(reason);
22
43
  } catch {}
23
- router.serverSsr?.cleanup();
24
44
  }
25
45
  };
26
46
  }
27
- async function replaceSsrResponse(result, response, reason) {
47
+ function bindSsrResponseToRequest(router, result, signal) {
28
48
  const ssrResponse = normalizeSsrResponse(result);
29
- if (ssrResponse.serverSsrCleanup === "stream") await ssrResponse.dispose(reason);
49
+ if (ssrResponse.serverSsrCleanup !== "stream") {
50
+ if (signal.aborted) disposeSsrResponseDetached(result, signal.reason);
51
+ return ssrResponse;
52
+ }
53
+ const failed = (error) => {
54
+ router?.serverSsr?.cleanup();
55
+ console.error(error);
56
+ };
57
+ const abort = () => {
58
+ disposeSsrResponseDetached(ssrResponse, signal.reason, failed);
59
+ };
60
+ if (signal.aborted) {
61
+ abort();
62
+ return ssrResponse;
63
+ }
64
+ signal.addEventListener("abort", abort, { once: true });
65
+ router?.serverSsr?.onCleanup(() => {
66
+ signal.removeEventListener("abort", abort);
67
+ });
68
+ return ssrResponse;
69
+ }
70
+ async function replaceSsrResponse(result, response, reason) {
71
+ await disposeSsrResponse(normalizeSsrResponse(result), reason);
30
72
  return {
31
73
  response,
32
74
  serverSsrCleanup: "none"
@@ -34,7 +76,7 @@ async function replaceSsrResponse(result, response, reason) {
34
76
  }
35
77
  async function stripSsrResponseBody(result, reason) {
36
78
  const ssrResponse = normalizeSsrResponse(result);
37
- if (ssrResponse.serverSsrCleanup === "stream") await ssrResponse.dispose(reason);
79
+ await disposeSsrResponse(ssrResponse, reason);
38
80
  return {
39
81
  response: new Response(null, ssrResponse.response),
40
82
  serverSsrCleanup: "none"
@@ -44,6 +86,6 @@ function defineHandlerCallback(handler) {
44
86
  return handler;
45
87
  }
46
88
  //#endregion
47
- export { createSsrStreamResponse, defineHandlerCallback, isSsrResponse, normalizeSsrResponse, replaceSsrResponse, stripSsrResponseBody };
89
+ export { bindSsrResponseToRequest, createSsrStreamResponse, defineHandlerCallback, disposeSsrResponse, disposeSsrResponseDetached, isSsrResponse, normalizeSsrResponse, replaceSsrResponse, stripSsrResponseBody };
48
90
 
49
91
  //# sourceMappingURL=handlerCallback.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"handlerCallback.js","names":[],"sources":["../../../src/ssr/handlerCallback.ts"],"sourcesContent":["import type { AnyRouter } from '../router'\n\nexport type SsrResponse =\n | {\n response: Response\n serverSsrCleanup: 'none'\n }\n | {\n response: Response\n serverSsrCleanup: 'stream'\n dispose: (reason?: unknown) => Promise<void>\n }\n\nexport type HandlerCallbackResult = Response | SsrResponse\n\nexport function isSsrResponse(value: unknown): value is SsrResponse {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'response' in value &&\n 'serverSsrCleanup' in value\n )\n}\n\nexport function normalizeSsrResponse(\n result: HandlerCallbackResult,\n): SsrResponse {\n return isSsrResponse(result)\n ? result\n : { response: result, serverSsrCleanup: 'none' }\n}\n\nexport function createSsrStreamResponse<TRouter extends AnyRouter>(\n router: TRouter,\n response: Response,\n): SsrResponse {\n if (!response.body) {\n throw new Error('Invariant failed: SSR stream response requires a body')\n }\n\n let disposed = false\n return {\n response,\n serverSsrCleanup: 'stream',\n async dispose(reason?: unknown) {\n if (disposed) return\n disposed = true\n\n try {\n await response.body!.cancel(reason)\n } catch {\n // ignore; fallback cleanup below still releases router SSR state\n }\n\n router.serverSsr?.cleanup()\n },\n }\n}\n\nexport async function replaceSsrResponse(\n result: HandlerCallbackResult,\n response: Response,\n reason?: unknown,\n): Promise<SsrResponse> {\n const ssrResponse = normalizeSsrResponse(result)\n if (ssrResponse.serverSsrCleanup === 'stream') {\n await ssrResponse.dispose(reason)\n }\n return { response, serverSsrCleanup: 'none' }\n}\n\nexport async function stripSsrResponseBody(\n result: HandlerCallbackResult,\n reason?: unknown,\n): Promise<SsrResponse> {\n const ssrResponse = normalizeSsrResponse(result)\n if (ssrResponse.serverSsrCleanup === 'stream') {\n await ssrResponse.dispose(reason)\n }\n return {\n response: new Response(null, ssrResponse.response),\n serverSsrCleanup: 'none',\n }\n}\n\nexport interface HandlerCallback<TRouter extends AnyRouter> {\n (ctx: {\n request: Request\n router: TRouter\n responseHeaders: Headers\n }): HandlerCallbackResult | Promise<HandlerCallbackResult>\n}\n\nexport function defineHandlerCallback<TRouter extends AnyRouter>(\n handler: HandlerCallback<TRouter>,\n): HandlerCallback<TRouter> {\n return handler\n}\n"],"mappings":";AAeA,SAAgB,cAAc,OAAsC;CAClE,OACE,OAAO,UAAU,YACjB,UAAU,QACV,cAAc,SACd,sBAAsB;AAE1B;AAEA,SAAgB,qBACd,QACa;CACb,OAAO,cAAc,MAAM,IACvB,SACA;EAAE,UAAU;EAAQ,kBAAkB;CAAO;AACnD;AAEA,SAAgB,wBACd,QACA,UACa;CACb,IAAI,CAAC,SAAS,MACZ,MAAM,IAAI,MAAM,uDAAuD;CAGzE,IAAI,WAAW;CACf,OAAO;EACL;EACA,kBAAkB;EAClB,MAAM,QAAQ,QAAkB;GAC9B,IAAI,UAAU;GACd,WAAW;GAEX,IAAI;IACF,MAAM,SAAS,KAAM,OAAO,MAAM;GACpC,QAAQ,CAER;GAEA,OAAO,WAAW,QAAQ;EAC5B;CACF;AACF;AAEA,eAAsB,mBACpB,QACA,UACA,QACsB;CACtB,MAAM,cAAc,qBAAqB,MAAM;CAC/C,IAAI,YAAY,qBAAqB,UACnC,MAAM,YAAY,QAAQ,MAAM;CAElC,OAAO;EAAE;EAAU,kBAAkB;CAAO;AAC9C;AAEA,eAAsB,qBACpB,QACA,QACsB;CACtB,MAAM,cAAc,qBAAqB,MAAM;CAC/C,IAAI,YAAY,qBAAqB,UACnC,MAAM,YAAY,QAAQ,MAAM;CAElC,OAAO;EACL,UAAU,IAAI,SAAS,MAAM,YAAY,QAAQ;EACjD,kBAAkB;CACpB;AACF;AAUA,SAAgB,sBACd,SAC0B;CAC1B,OAAO;AACT"}
1
+ {"version":3,"file":"handlerCallback.js","names":[],"sources":["../../../src/ssr/handlerCallback.ts"],"sourcesContent":["import type { AnyRouter } from '../router'\n\nexport type SsrResponse =\n | {\n response: Response\n serverSsrCleanup: 'none'\n }\n | {\n response: Response\n serverSsrCleanup: 'stream'\n dispose: (reason?: unknown) => Promise<void>\n }\n\nexport type HandlerCallbackResult = Response | SsrResponse\n\nexport function isSsrResponse(value: unknown): value is SsrResponse {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'response' in value &&\n 'serverSsrCleanup' in value\n )\n}\n\nexport function normalizeSsrResponse(\n result: HandlerCallbackResult,\n): SsrResponse {\n return isSsrResponse(result)\n ? result\n : { response: result, serverSsrCleanup: 'none' }\n}\n\nexport function disposeSsrResponse(\n response: SsrResponse,\n reason?: unknown,\n): Promise<void> {\n if (response.serverSsrCleanup !== 'stream') {\n return Promise.resolve()\n }\n try {\n return Promise.resolve(response.dispose(reason))\n } catch (error) {\n return Promise.reject(error)\n }\n}\n\nexport function disposeSsrResponseDetached(\n result: HandlerCallbackResult,\n reason?: unknown,\n onError: (error: unknown) => void = console.error,\n): void {\n const ssrResponse = normalizeSsrResponse(result)\n if (ssrResponse.serverSsrCleanup === 'stream') {\n void disposeSsrResponse(ssrResponse, reason).catch(onError)\n return\n }\n\n if (ssrResponse.response.body) {\n try {\n void ssrResponse.response.body.cancel(reason).catch(onError)\n } catch (error) {\n onError(error)\n }\n }\n}\n\nexport function createSsrStreamResponse<TRouter extends AnyRouter>(\n router: TRouter,\n response: Response,\n): SsrResponse {\n if (!response.body) {\n throw new Error('Invariant failed: SSR stream response requires a body')\n }\n\n let disposed = false\n return {\n response,\n serverSsrCleanup: 'stream',\n async dispose(reason?: unknown) {\n if (disposed) {\n return\n }\n disposed = true\n\n // Sever router ownership before asking user/renderer stream machinery to\n // cancel. A custom stream is allowed to ignore cancellation forever.\n router.serverSsr?.cleanup()\n\n try {\n await response.body!.cancel(reason)\n } catch {\n // Cleanup above already released router SSR state.\n }\n },\n }\n}\n\nexport function bindSsrResponseToRequest(\n router: AnyRouter | undefined,\n result: HandlerCallbackResult,\n signal: AbortSignal,\n): SsrResponse {\n const ssrResponse = normalizeSsrResponse(result)\n if (ssrResponse.serverSsrCleanup !== 'stream') {\n if (signal.aborted) {\n disposeSsrResponseDetached(result, signal.reason)\n }\n return ssrResponse\n }\n\n const failed = (error: unknown) => {\n router?.serverSsr?.cleanup()\n console.error(error)\n }\n const abort = () => {\n disposeSsrResponseDetached(ssrResponse, signal.reason, failed)\n }\n if (signal.aborted) {\n abort()\n return ssrResponse\n }\n\n signal.addEventListener('abort', abort, { once: true })\n router?.serverSsr?.onCleanup(() => {\n signal.removeEventListener('abort', abort)\n })\n return ssrResponse\n}\n\nexport async function replaceSsrResponse(\n result: HandlerCallbackResult,\n response: Response,\n reason?: unknown,\n): Promise<SsrResponse> {\n const ssrResponse = normalizeSsrResponse(result)\n await disposeSsrResponse(ssrResponse, reason)\n return { response, serverSsrCleanup: 'none' }\n}\n\nexport async function stripSsrResponseBody(\n result: HandlerCallbackResult,\n reason?: unknown,\n): Promise<SsrResponse> {\n const ssrResponse = normalizeSsrResponse(result)\n await disposeSsrResponse(ssrResponse, reason)\n return {\n response: new Response(null, ssrResponse.response),\n serverSsrCleanup: 'none',\n }\n}\n\nexport interface HandlerCallback<TRouter extends AnyRouter> {\n (ctx: {\n request: Request\n router: TRouter\n responseHeaders: Headers\n }): HandlerCallbackResult | Promise<HandlerCallbackResult>\n}\n\nexport function defineHandlerCallback<TRouter extends AnyRouter>(\n handler: HandlerCallback<TRouter>,\n): HandlerCallback<TRouter> {\n return handler\n}\n"],"mappings":";AAeA,SAAgB,cAAc,OAAsC;CAClE,OACE,OAAO,UAAU,YACjB,UAAU,QACV,cAAc,SACd,sBAAsB;AAE1B;AAEA,SAAgB,qBACd,QACa;CACb,OAAO,cAAc,MAAM,IACvB,SACA;EAAE,UAAU;EAAQ,kBAAkB;CAAO;AACnD;AAEA,SAAgB,mBACd,UACA,QACe;CACf,IAAI,SAAS,qBAAqB,UAChC,OAAO,QAAQ,QAAQ;CAEzB,IAAI;EACF,OAAO,QAAQ,QAAQ,SAAS,QAAQ,MAAM,CAAC;CACjD,SAAS,OAAO;EACd,OAAO,QAAQ,OAAO,KAAK;CAC7B;AACF;AAEA,SAAgB,2BACd,QACA,QACA,UAAoC,QAAQ,OACtC;CACN,MAAM,cAAc,qBAAqB,MAAM;CAC/C,IAAI,YAAY,qBAAqB,UAAU;EAC7C,mBAAwB,aAAa,MAAM,EAAE,MAAM,OAAO;EAC1D;CACF;CAEA,IAAI,YAAY,SAAS,MACvB,IAAI;EACF,YAAiB,SAAS,KAAK,OAAO,MAAM,EAAE,MAAM,OAAO;CAC7D,SAAS,OAAO;EACd,QAAQ,KAAK;CACf;AAEJ;AAEA,SAAgB,wBACd,QACA,UACa;CACb,IAAI,CAAC,SAAS,MACZ,MAAM,IAAI,MAAM,uDAAuD;CAGzE,IAAI,WAAW;CACf,OAAO;EACL;EACA,kBAAkB;EAClB,MAAM,QAAQ,QAAkB;GAC9B,IAAI,UACF;GAEF,WAAW;GAIX,OAAO,WAAW,QAAQ;GAE1B,IAAI;IACF,MAAM,SAAS,KAAM,OAAO,MAAM;GACpC,QAAQ,CAER;EACF;CACF;AACF;AAEA,SAAgB,yBACd,QACA,QACA,QACa;CACb,MAAM,cAAc,qBAAqB,MAAM;CAC/C,IAAI,YAAY,qBAAqB,UAAU;EAC7C,IAAI,OAAO,SACT,2BAA2B,QAAQ,OAAO,MAAM;EAElD,OAAO;CACT;CAEA,MAAM,UAAU,UAAmB;EACjC,QAAQ,WAAW,QAAQ;EAC3B,QAAQ,MAAM,KAAK;CACrB;CACA,MAAM,cAAc;EAClB,2BAA2B,aAAa,OAAO,QAAQ,MAAM;CAC/D;CACA,IAAI,OAAO,SAAS;EAClB,MAAM;EACN,OAAO;CACT;CAEA,OAAO,iBAAiB,SAAS,OAAO,EAAE,MAAM,KAAK,CAAC;CACtD,QAAQ,WAAW,gBAAgB;EACjC,OAAO,oBAAoB,SAAS,KAAK;CAC3C,CAAC;CACD,OAAO;AACT;AAEA,eAAsB,mBACpB,QACA,UACA,QACsB;CAEtB,MAAM,mBADc,qBAAqB,MAChB,GAAa,MAAM;CAC5C,OAAO;EAAE;EAAU,kBAAkB;CAAO;AAC9C;AAEA,eAAsB,qBACpB,QACA,QACsB;CACtB,MAAM,cAAc,qBAAqB,MAAM;CAC/C,MAAM,mBAAmB,aAAa,MAAM;CAC5C,OAAO;EACL,UAAU,IAAI,SAAS,MAAM,YAAY,QAAQ;EACjD,kBAAkB;CACpB;AACF;AAUA,SAAgB,sBACd,SAC0B;CAC1B,OAAO;AACT"}
@@ -1,6 +1,6 @@
1
- export { createRequestHandler } from './createRequestHandler.js';
1
+ export { createRequestHandler, waitForRequest } from './createRequestHandler.js';
2
2
  export type { RequestHandler } from './createRequestHandler.js';
3
- export { createSsrStreamResponse, defineHandlerCallback, isSsrResponse, normalizeSsrResponse, replaceSsrResponse, stripSsrResponseBody, } from './handlerCallback.js';
3
+ export { bindSsrResponseToRequest, createSsrStreamResponse, defineHandlerCallback, disposeSsrResponse, disposeSsrResponseDetached, isSsrResponse, normalizeSsrResponse, replaceSsrResponse, stripSsrResponseBody, } from './handlerCallback.js';
4
4
  export type { HandlerCallback, HandlerCallbackResult, SsrResponse, } from './handlerCallback.js';
5
5
  export { transformPipeableStreamWithRouter, transformStreamWithRouter, transformReadableStreamWithRouter, } from './transformStreamWithRouter.js';
6
6
  export type { TransformStreamWithRouterOptions } from './transformStreamWithRouter.js';
@@ -1,5 +1,5 @@
1
1
  import { attachRouterServerSsrUtils, getNormalizedURL, getOrigin } from "./ssr-server.js";
2
- import { createSsrStreamResponse, defineHandlerCallback, isSsrResponse, normalizeSsrResponse, replaceSsrResponse, stripSsrResponseBody } from "./handlerCallback.js";
3
- import { createRequestHandler } from "./createRequestHandler.js";
2
+ import { bindSsrResponseToRequest, createSsrStreamResponse, defineHandlerCallback, disposeSsrResponse, disposeSsrResponseDetached, isSsrResponse, normalizeSsrResponse, replaceSsrResponse, stripSsrResponseBody } from "./handlerCallback.js";
3
+ import { createRequestHandler, waitForRequest } from "./createRequestHandler.js";
4
4
  import { transformPipeableStreamWithRouter, transformReadableStreamWithRouter, transformStreamWithRouter } from "./transformStreamWithRouter.js";
5
- export { attachRouterServerSsrUtils, createRequestHandler, createSsrStreamResponse, defineHandlerCallback, getNormalizedURL, getOrigin, isSsrResponse, normalizeSsrResponse, replaceSsrResponse, stripSsrResponseBody, transformPipeableStreamWithRouter, transformReadableStreamWithRouter, transformStreamWithRouter };
5
+ export { attachRouterServerSsrUtils, bindSsrResponseToRequest, createRequestHandler, createSsrStreamResponse, defineHandlerCallback, disposeSsrResponse, disposeSsrResponseDetached, getNormalizedURL, getOrigin, isSsrResponse, normalizeSsrResponse, replaceSsrResponse, stripSsrResponseBody, transformPipeableStreamWithRouter, transformReadableStreamWithRouter, transformStreamWithRouter, waitForRequest };
@@ -1,10 +1 @@
1
- import { GLOBAL_SEROVAL, GLOBAL_TSR } from './constants.js';
2
- import { TsrSsrGlobal } from './types.js';
3
- import { AnyRouter } from '../router.js';
4
- declare global {
5
- interface Window {
6
- [GLOBAL_TSR]?: TsrSsrGlobal;
7
- [GLOBAL_SEROVAL]?: any;
8
- }
9
- }
10
- export declare function hydrate(router: AnyRouter): Promise<any>;
1
+ export { hydrate } from '../load-client.js';
@@ -1,9 +1,9 @@
1
1
  //#region src/ssr/ssr-match-id.ts
2
2
  function dehydrateSsrMatchId(id) {
3
- return id.replaceAll("/", "\0");
3
+ return id.replaceAll("~", "~~").replaceAll("\0", "~0").replaceAll("�", "~r").replaceAll("/", "\0");
4
4
  }
5
5
  function hydrateSsrMatchId(id) {
6
- return id.replaceAll("\0", "/").replaceAll("�", "/");
6
+ return id.replaceAll("\0", "/").replaceAll("�", "/").replace(/~([~0r])/g, (_, code) => code === "0" ? "\0" : code === "r" ? "�" : code);
7
7
  }
8
8
  //#endregion
9
9
  export { dehydrateSsrMatchId, hydrateSsrMatchId };
@@ -1 +1 @@
1
- {"version":3,"file":"ssr-match-id.js","names":[],"sources":["../../../src/ssr/ssr-match-id.ts"],"sourcesContent":["export function dehydrateSsrMatchId(id: string): string {\n return id.replaceAll('/', '\\0')\n}\n\nexport function hydrateSsrMatchId(id: string): string {\n return id.replaceAll('\\0', '/').replaceAll('\\uFFFD', '/')\n}\n"],"mappings":";AAAA,SAAgB,oBAAoB,IAAoB;CACtD,OAAO,GAAG,WAAW,KAAK,IAAI;AAChC;AAEA,SAAgB,kBAAkB,IAAoB;CACpD,OAAO,GAAG,WAAW,MAAM,GAAG,EAAE,WAAW,KAAU,GAAG;AAC1D"}
1
+ {"version":3,"file":"ssr-match-id.js","names":[],"sources":["../../../src/ssr/ssr-match-id.ts"],"sourcesContent":["export function dehydrateSsrMatchId(id: string): string {\n return id\n .replaceAll('~', '~~')\n .replaceAll('\\0', '~0')\n .replaceAll('\\uFFFD', '~r')\n .replaceAll('/', '\\0')\n}\n\nexport function hydrateSsrMatchId(id: string): string {\n return id\n .replaceAll('\\0', '/')\n .replaceAll('\\uFFFD', '/')\n .replace(/~([~0r])/g, (_, code) =>\n code === '0' ? '\\0' : code === 'r' ? '\\uFFFD' : code,\n )\n}\n"],"mappings":";AAAA,SAAgB,oBAAoB,IAAoB;CACtD,OAAO,GACJ,WAAW,KAAK,IAAI,EACpB,WAAW,MAAM,IAAI,EACrB,WAAW,KAAU,IAAI,EACzB,WAAW,KAAK,IAAI;AACzB;AAEA,SAAgB,kBAAkB,IAAoB;CACpD,OAAO,GACJ,WAAW,MAAM,GAAG,EACpB,WAAW,KAAU,GAAG,EACxB,QAAQ,cAAc,GAAG,SACxB,SAAS,MAAM,OAAO,SAAS,MAAM,MAAW,IAClD;AACJ"}
@@ -2,11 +2,12 @@ import { decodePath } from "../utils.js";
2
2
  import { invariant } from "../invariant.js";
3
3
  import { createLRUCache } from "../lru-cache.js";
4
4
  import { rootRouteId } from "../root.js";
5
+ import { dehydrateSsrMatchId } from "./ssr-match-id.js";
6
+ import { _getRenderedMatches } from "../load-client.js";
5
7
  import { createInlineCssPlaceholderAsset, createInlineCssStyleAsset, getStylesheetHref } from "../manifest.js";
6
8
  import { GLOBAL_TSR, TSR_SCRIPT_BARRIER_ID } from "./constants.js";
7
9
  import { makeSsrSerovalPlugin } from "./serializer/transformer.js";
8
10
  import { defaultSerovalPlugins } from "./serializer/seroval-plugins.js";
9
- import { dehydrateSsrMatchId } from "./ssr-match-id.js";
10
11
  import tsrScript_default from "./tsrScript.js";
11
12
  import { crossSerializeStream, getCrossReferenceHeader } from "seroval";
12
13
  //#region src/ssr/ssr-server.ts
@@ -26,7 +27,7 @@ function dehydrateMatch(match) {
26
27
  ["error", "e"],
27
28
  ["ssr", "ssr"]
28
29
  ]) if (match[key] !== void 0) dehydratedMatch[shorthand] = match[key];
29
- if (match.globalNotFound) dehydratedMatch.g = true;
30
+ if (match._notFound) dehydratedMatch.g = true;
30
31
  return dehydratedMatch;
31
32
  }
32
33
  const INITIAL_SCRIPTS = [getCrossReferenceHeader(SCOPE_ID), tsrScript_default];
@@ -223,7 +224,7 @@ function attachRouterServerSsrUtils({ router, manifest, getRequestAssets }) {
223
224
  router.ssr = { get manifest() {
224
225
  if (!manifest) return manifest;
225
226
  const requestAssets = getRequestAssets?.();
226
- const matches = router.stores.matches.get();
227
+ const matches = _getRenderedMatches(router.stores.matches.get());
227
228
  const hasAssets = hasRequestAssets(requestAssets);
228
229
  if (!hasAssets && !manifest.inlineCss) return manifest;
229
230
  let inlineCssAsset;
@@ -291,7 +292,7 @@ function attachRouterServerSsrUtils({ router, manifest, getRequestAssets }) {
291
292
  if (process.env.NODE_ENV !== "production") throw new Error("Invariant failed: router is already dehydrated!");
292
293
  invariant();
293
294
  }
294
- let matchesToDehydrate = router.stores.matches.get();
295
+ let matchesToDehydrate = _getRenderedMatches(router.stores.matches.get());
295
296
  if (router.isShell()) matchesToDehydrate = matchesToDehydrate.slice(0, 1);
296
297
  const matches = matchesToDehydrate.map(dehydrateMatch);
297
298
  let manifestToDehydrate = void 0;
@@ -316,9 +317,8 @@ function attachRouterServerSsrUtils({ router, manifest, getRequestAssets }) {
316
317
  manifest: manifestToDehydrate,
317
318
  matches
318
319
  };
319
- const lastMatchId = matchesToDehydrate[matchesToDehydrate.length - 1]?.id;
320
- if (lastMatchId) dehydratedRouter.lastMatchId = dehydrateSsrMatchId(lastMatchId);
321
320
  const dehydratedData = await router.options.dehydrate?.();
321
+ if (cleanupStarted) return;
322
322
  if (dehydratedData) dehydratedRouter.dehydratedData = dehydratedData;
323
323
  _dehydrated = true;
324
324
  const trackPlugins = { didRun: false };
@@ -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} 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, ServerSsr } from '../router'\nimport type { AnyRouteMatch } from '../Matches'\nimport type {\n Manifest,\n ManifestRoute,\n ManifestRouteAssets,\n RouterManagedTag,\n ServerManifest,\n} from '../manifest'\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 injectScript: ((script: string) => void) | undefined\n private _queue: Array<string>\n private _scriptBarrierLifted = false\n private _cleanedUp = false\n private _microtaskVersion = 0\n private _pendingMicrotaskVersion = 0\n\n constructor(injectScript: (script: string) => void) {\n this.injectScript = injectScript\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 (this._scriptBarrierLifted) {\n this.scheduleInjectBufferedScripts()\n }\n }\n\n liftBarrier() {\n if (this._scriptBarrierLifted || this._cleanedUp) return\n this._scriptBarrierLifted = true\n if (this._queue.length > 0) {\n this.scheduleInjectBufferedScripts()\n }\n }\n\n scheduleInjectBufferedScripts() {\n if (this._pendingMicrotaskVersion !== 0) return\n const pendingVersion = ++this._microtaskVersion\n this._pendingMicrotaskVersion = pendingVersion\n queueMicrotask(() => {\n if (this._pendingMicrotaskVersion !== pendingVersion) return\n this._pendingMicrotaskVersion = 0\n this.injectBufferedScripts()\n })\n }\n\n clearPendingMicrotask() {\n if (this._pendingMicrotaskVersion === 0) return\n this._pendingMicrotaskVersion = 0\n this._microtaskVersion++\n }\n\n /**\n * Flushes any pending scripts synchronously.\n * Call this before signaling serialization finished 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.clearPendingMicrotask()\n this.injectBufferedScripts()\n }\n\n takeAll() {\n return this.takeScripts(this._queue.length)\n }\n\n takeScripts(count: number) {\n if (count <= 0) return undefined\n const bufferedScripts = this._queue.splice(0, count)\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 hasPending() {\n return this._queue.length > 0\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) {\n this.injectScript?.(scriptsToInject)\n }\n }\n\n cleanup() {\n this._cleanedUp = true\n this.clearPendingMicrotask()\n this._queue = []\n this.injectScript = undefined\n }\n}\n\nconst isProd = process.env.NODE_ENV === 'production'\n\ntype FilteredRoutes = Manifest['routes']\n\ntype PreparedMatchedManifestRoutes = {\n routes: FilteredRoutes\n hasStrippedRoutes: boolean\n inlineCssHrefs?: Array<string>\n inlineCss?: string\n}\n\ntype ManifestLRU = LRUCache<string, PreparedMatchedManifestRoutes>\n\nconst MANIFEST_CACHE_SIZE = 100\nconst manifestCaches = new WeakMap<ServerManifest, ManifestLRU>()\n\nfunction getManifestCache(manifest: ServerManifest): ManifestLRU {\n const cache = manifestCaches.get(manifest)\n if (cache) return cache\n const newCache = createLRUCache<string, PreparedMatchedManifestRoutes>(\n MANIFEST_CACHE_SIZE,\n )\n manifestCaches.set(manifest, newCache)\n return newCache\n}\n\nfunction getInlineCssForPreparedRoutes(\n manifest: ServerManifest,\n preparedRoutes: PreparedMatchedManifestRoutes,\n) {\n if (preparedRoutes.inlineCss !== undefined) return preparedRoutes.inlineCss\n\n const styles = manifest.inlineCss?.styles\n const hrefs = preparedRoutes.inlineCssHrefs\n if (!styles || !hrefs?.length) return undefined\n\n let css = ''\n for (const href of hrefs) {\n css += styles[href]!\n }\n\n preparedRoutes.inlineCss = css\n return css\n}\n\nfunction getInlineCssAssetForPreparedRoutes(\n manifest: ServerManifest,\n preparedRoutes: PreparedMatchedManifestRoutes,\n) {\n const css = getInlineCssForPreparedRoutes(manifest, preparedRoutes)\n\n return css === undefined ? undefined : createInlineCssStyleAsset(css)\n}\n\nfunction getMatchedRoutesCacheKey(matches: Array<AnyRouteMatch>) {\n let cacheKey = ''\n for (let i = 0; i < matches.length; i++) {\n cacheKey += (i === 0 ? '' : '\\0') + matches[i]!.routeId\n }\n return cacheKey\n}\n\nfunction getPreparedMatchedManifestRoutes(\n manifest: ServerManifest,\n matches: Array<AnyRouteMatch>,\n cacheKey: string,\n) {\n if (isProd) {\n const cached = getManifestCache(manifest).get(cacheKey)\n if (cached) {\n return cached\n }\n }\n\n const preparedRoutes = prepareMatchedManifestRoutes(manifest, matches)\n\n if (isProd) {\n getManifestCache(manifest).set(cacheKey, preparedRoutes)\n }\n\n return preparedRoutes\n}\n\nfunction prepareMatchedManifestRoutes(\n manifest: ServerManifest,\n matches: Array<AnyRouteMatch>,\n): PreparedMatchedManifestRoutes {\n const inlineStyles = manifest.inlineCss?.styles\n const routes: FilteredRoutes = {}\n\n if (!inlineStyles) {\n for (const match of matches) {\n const route = manifest.routes[match.routeId]\n if (route) {\n routes[match.routeId] = route\n }\n }\n return { routes, hasStrippedRoutes: false }\n }\n\n const inlineCssHrefs: Array<string> = []\n const seenInlineCssHrefs = new Set<string>()\n let hasStrippedRoutes = false\n\n for (const match of matches) {\n const routeId = match.routeId\n const route = manifest.routes[routeId]\n if (!route) {\n continue\n }\n\n const nextRoute = stripInlinedStylesheetAssetsFromRoute(\n inlineStyles,\n route,\n inlineCssHrefs,\n seenInlineCssHrefs,\n )\n\n if (nextRoute !== route) {\n hasStrippedRoutes = true\n }\n routes[routeId] = nextRoute\n }\n\n return {\n routes,\n hasStrippedRoutes,\n ...(inlineCssHrefs.length ? { inlineCssHrefs } : {}),\n }\n}\n\nfunction stripInlinedStylesheetAssetsFromRoute(\n inlineStyles: Record<string, string>,\n route: ManifestRoute,\n inlineCssHrefs: Array<string>,\n seenInlineCssHrefs: Set<string>,\n): ManifestRoute {\n const css = route.css\n if (!css) {\n return route\n }\n\n if (css.length === 0) {\n const nextRoute = { ...route }\n delete nextRoute.css\n return nextRoute\n }\n\n let cssLinks: typeof css | undefined\n for (let i = 0; i < css.length; i++) {\n const link = css[i]!\n const href = getStylesheetHref(link)\n if (inlineStyles[href] === undefined) {\n if (cssLinks) {\n cssLinks.push(link)\n }\n continue\n }\n\n if (!seenInlineCssHrefs.has(href)) {\n seenInlineCssHrefs.add(href)\n inlineCssHrefs.push(href)\n }\n\n if (!cssLinks) {\n cssLinks = css.slice(0, i)\n }\n }\n\n if (!cssLinks) {\n return route\n }\n\n if (cssLinks.length > 0) {\n return { ...route, css: cssLinks }\n }\n\n const nextRoute = { ...route }\n delete nextRoute.css\n return nextRoute\n}\n\nfunction hasRouteAssets(route: ManifestRoute) {\n return !!route.scripts?.length || !!route.css?.length\n}\n\nfunction hasRequestAssets(assets: ManifestRouteAssets | undefined) {\n return !!assets && (!!assets.preloads?.length || hasRouteAssets(assets))\n}\n\nfunction mergeRequestAssetsIntoRootRoute(\n rootRoute: ManifestRoute | undefined,\n requestAssets: ManifestRouteAssets | undefined,\n): ManifestRoute {\n const preloads = requestAssets?.preloads?.length\n ? [...requestAssets.preloads, ...(rootRoute?.preloads ?? [])]\n : rootRoute?.preloads\n const scripts = requestAssets?.scripts?.length\n ? [...requestAssets.scripts, ...(rootRoute?.scripts ?? [])]\n : rootRoute?.scripts\n const cssLinks = requestAssets?.css?.length\n ? [...requestAssets.css, ...(rootRoute?.css ?? [])]\n : rootRoute?.css\n\n return {\n ...(rootRoute ?? {}),\n ...(preloads?.length ? { preloads } : {}),\n ...(scripts?.length ? { scripts } : {}),\n ...(cssLinks?.length ? { css: cssLinks } : {}),\n }\n}\n\nexport function attachRouterServerSsrUtils({\n router,\n manifest,\n getRequestAssets,\n}: {\n router: AnyRouter\n manifest: ServerManifest | undefined\n getRequestAssets?: () => ManifestRouteAssets | undefined\n}) {\n router.ssr = {\n get manifest() {\n if (!manifest) return manifest\n\n const requestAssets = getRequestAssets?.()\n const matches = router.stores.matches.get()\n const hasAssets = hasRequestAssets(requestAssets)\n\n if (!hasAssets && !manifest.inlineCss) {\n return manifest\n }\n\n let inlineCssAsset: Manifest['inlineStyle'] | undefined\n let routes = manifest.routes\n if (manifest.inlineCss) {\n const cacheKey = getMatchedRoutesCacheKey(matches)\n const preparedManifest = getPreparedMatchedManifestRoutes(\n manifest,\n matches,\n cacheKey,\n )\n inlineCssAsset = getInlineCssAssetForPreparedRoutes(\n manifest,\n preparedManifest,\n )\n if (preparedManifest.hasStrippedRoutes) {\n routes = { ...manifest.routes, ...preparedManifest.routes }\n }\n }\n\n if (!hasAssets) {\n return {\n ...(manifest.scriptFormat\n ? { scriptFormat: manifest.scriptFormat }\n : {}),\n ...(inlineCssAsset ? { inlineStyle: inlineCssAsset } : {}),\n routes,\n }\n }\n\n const rootRoute = routes[rootRouteId]\n\n // Merge request-scoped assets into root route without mutating cached manifest\n return {\n ...(manifest.scriptFormat\n ? { scriptFormat: manifest.scriptFormat }\n : {}),\n ...(inlineCssAsset ? { inlineStyle: inlineCssAsset } : {}),\n routes: {\n ...routes,\n [rootRouteId]: mergeRequestAssetsIntoRootRoute(\n rootRoute,\n requestAssets,\n ),\n },\n }\n },\n }\n let _dehydrated = false\n let _serializationFinished = false\n let streamFastPathReserved = false\n const renderFinishedListeners: Array<() => void> = []\n const injectedHtmlListeners: Array<() => void> = []\n const serializationFinishedListeners: Array<() => void> = []\n const cleanupListeners: Array<() => void> = []\n let cleanupStarted = false\n let injectedHtmlBuffer = ''\n\n const callListeners = (listeners: Array<() => void>, errorPrefix: string) => {\n const snapshot = listeners.slice()\n for (const l of snapshot) {\n try {\n l()\n } catch (err) {\n console.error(`${errorPrefix}:`, err)\n }\n }\n }\n\n const removeListener = (\n listeners: Array<() => void>,\n listener: () => void,\n ) => {\n const index = listeners.indexOf(listener)\n if (index >= 0) listeners.splice(index, 1)\n }\n\n const scriptBuffer = new ScriptBuffer((script) => {\n serverSsr.injectScript(script)\n })\n\n const serverSsr: ServerSsr = {\n injectHtml: (html: string) => {\n if (!html || cleanupStarted) return\n // Buffer the HTML so it can be retrieved via takeBufferedHtml()\n injectedHtmlBuffer += html\n callListeners(injectedHtmlListeners, 'SSR injected HTML listener error')\n },\n injectScript: (script: string) => {\n if (!script || cleanupStarted) return\n const html = `<script${router.options.ssr?.nonce ? ` nonce='${router.options.ssr.nonce}'` : ''}>${script}</script>`\n serverSsr.injectHtml(html)\n },\n dehydrate: async (opts?: { requestAssets?: ManifestRouteAssets }) => {\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 // Only currently matched routes are dehydrated. Other route assets are\n // loaded through dynamic imports when those routes become active.\n if (manifest) {\n const cacheKey = getMatchedRoutesCacheKey(matchesToDehydrate)\n const preparedManifest = getPreparedMatchedManifestRoutes(\n manifest,\n matchesToDehydrate,\n cacheKey,\n )\n\n manifestToDehydrate = {\n ...(manifest.scriptFormat\n ? { scriptFormat: manifest.scriptFormat }\n : {}),\n ...(preparedManifest.inlineCssHrefs\n ? { inlineStyle: createInlineCssPlaceholderAsset() }\n : {}),\n routes: preparedManifest.routes,\n }\n\n // Merge request-scoped assets into root route (without mutating cached manifest)\n const requestAssets = opts?.requestAssets\n if (hasRequestAssets(requestAssets)) {\n const existingRoot = manifestToDehydrate.routes[rootRouteId]\n manifestToDehydrate.routes = {\n ...manifestToDehydrate.routes,\n [rootRouteId]: mergeRequestAssetsIntoRootRoute(\n existingRoot,\n requestAssets,\n ),\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 let serializationCompleteSignaled = false\n const signalSerializationComplete = () => {\n if (serializationCompleteSignaled || cleanupStarted) return\n serializationCompleteSignaled = true\n _serializationFinished = true\n\n const listeners = serializationFinishedListeners.slice()\n serializationFinishedListeners.length = 0\n\n for (const l of listeners) {\n try {\n l()\n } catch (err) {\n console.error('Serialization listener error:', err)\n }\n }\n }\n\n const finishScriptSerialization = () => {\n if (serializationCompleteSignaled || cleanupStarted) return\n scriptBuffer.enqueue(GLOBAL_TSR + '.e()')\n // Must synchronously notify injected HTML listeners before signaling\n // completion; otherwise the held </body> tail could flush ahead of the\n // end script.\n scriptBuffer.flush()\n signalSerializationComplete()\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 finishScriptSerialization()\n },\n scopeId: SCOPE_ID,\n onDone: () => {\n finishScriptSerialization()\n },\n })\n },\n isDehydrated() {\n return _dehydrated\n },\n isSerializationFinished() {\n return _serializationFinished\n },\n reserveStreamFastPath() {\n if (\n !cleanupStarted &&\n _serializationFinished &&\n !streamFastPathReserved &&\n renderFinishedListeners.length === 0 &&\n !injectedHtmlBuffer &&\n !scriptBuffer.hasPending()\n ) {\n streamFastPathReserved = true\n return true\n }\n return false\n },\n onInjectedHtml: (listener) => {\n if (cleanupStarted) return () => {}\n injectedHtmlListeners.push(listener)\n return () => removeListener(injectedHtmlListeners, listener)\n },\n onRenderFinished: (listener) => {\n if (cleanupStarted || streamFastPathReserved) return\n renderFinishedListeners.push(listener)\n },\n onSerializationFinished: (listener) => {\n if (cleanupStarted) return () => {}\n if (_serializationFinished && !cleanupStarted) {\n try {\n listener()\n } catch (err) {\n console.error('Serialization listener error:', err)\n }\n return () => {}\n }\n serializationFinishedListeners.push(listener)\n return () => removeListener(serializationFinishedListeners, listener)\n },\n onCleanup: (listener) => {\n if (cleanupStarted) return\n cleanupListeners.push(listener)\n },\n setRenderFinished: () => {\n if (cleanupStarted) return\n scriptBuffer.liftBarrier()\n const listeners = renderFinishedListeners.slice()\n renderFinishedListeners.length = 0\n for (const l of listeners) {\n try {\n l()\n } catch (err) {\n console.error('Error in render finished listener:', err)\n }\n }\n if (_serializationFinished) {\n scriptBuffer.flush()\n }\n },\n takeBufferedScripts() {\n const scripts = scriptBuffer.takeAll()\n if (!scripts) return undefined\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/reentrant cleanup calls. A listener could call\n // cleanup() again indirectly; snapshot + clear before invoking so each\n // listener runs exactly once and reentry is a no-op.\n if (cleanupStarted) return\n cleanupStarted = true\n const listeners = cleanupListeners.slice()\n cleanupListeners.length = 0\n for (const l of listeners) {\n try {\n l()\n } catch (err) {\n console.error('Error in SSR cleanup listener:', err)\n }\n }\n renderFinishedListeners.length = 0\n injectedHtmlListeners.length = 0\n serializationFinishedListeners.length = 0\n injectedHtmlBuffer = ''\n scriptBuffer.cleanup()\n router.serverSsr = undefined\n },\n }\n\n router.serverSsr = serverSsr\n for (const listener of router.serverSsrLifecycle?.onServerSsrAttach ?? []) {\n try {\n listener(serverSsr)\n } catch (err) {\n console.error('SSR attach listener error:', err)\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} from '../manifest'\nimport { decodePath } from '../utils'\nimport { createLRUCache } from '../lru-cache'\nimport { rootRouteId } from '../root'\nimport { _getRenderedMatches } from '../load-client'\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, ServerSsr } from '../router'\nimport type { AnyRouteMatch } from '../Matches'\nimport type {\n Manifest,\n ManifestRoute,\n ManifestRouteAssets,\n RouterManagedTag,\n ServerManifest,\n} from '../manifest'\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._notFound) {\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 injectScript: ((script: string) => void) | undefined\n private _queue: Array<string>\n private _scriptBarrierLifted = false\n private _cleanedUp = false\n private _microtaskVersion = 0\n private _pendingMicrotaskVersion = 0\n\n constructor(injectScript: (script: string) => void) {\n this.injectScript = injectScript\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 (this._scriptBarrierLifted) {\n this.scheduleInjectBufferedScripts()\n }\n }\n\n liftBarrier() {\n if (this._scriptBarrierLifted || this._cleanedUp) return\n this._scriptBarrierLifted = true\n if (this._queue.length > 0) {\n this.scheduleInjectBufferedScripts()\n }\n }\n\n scheduleInjectBufferedScripts() {\n if (this._pendingMicrotaskVersion !== 0) return\n const pendingVersion = ++this._microtaskVersion\n this._pendingMicrotaskVersion = pendingVersion\n queueMicrotask(() => {\n if (this._pendingMicrotaskVersion !== pendingVersion) return\n this._pendingMicrotaskVersion = 0\n this.injectBufferedScripts()\n })\n }\n\n clearPendingMicrotask() {\n if (this._pendingMicrotaskVersion === 0) return\n this._pendingMicrotaskVersion = 0\n this._microtaskVersion++\n }\n\n /**\n * Flushes any pending scripts synchronously.\n * Call this before signaling serialization finished 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.clearPendingMicrotask()\n this.injectBufferedScripts()\n }\n\n takeAll() {\n return this.takeScripts(this._queue.length)\n }\n\n takeScripts(count: number) {\n if (count <= 0) return undefined\n const bufferedScripts = this._queue.splice(0, count)\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 hasPending() {\n return this._queue.length > 0\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) {\n this.injectScript?.(scriptsToInject)\n }\n }\n\n cleanup() {\n this._cleanedUp = true\n this.clearPendingMicrotask()\n this._queue = []\n this.injectScript = undefined\n }\n}\n\nconst isProd = process.env.NODE_ENV === 'production'\n\ntype FilteredRoutes = Manifest['routes']\n\ntype PreparedMatchedManifestRoutes = {\n routes: FilteredRoutes\n hasStrippedRoutes: boolean\n inlineCssHrefs?: Array<string>\n inlineCss?: string\n}\n\ntype ManifestLRU = LRUCache<string, PreparedMatchedManifestRoutes>\n\nconst MANIFEST_CACHE_SIZE = 100\nconst manifestCaches = new WeakMap<ServerManifest, ManifestLRU>()\n\nfunction getManifestCache(manifest: ServerManifest): ManifestLRU {\n const cache = manifestCaches.get(manifest)\n if (cache) return cache\n const newCache = createLRUCache<string, PreparedMatchedManifestRoutes>(\n MANIFEST_CACHE_SIZE,\n )\n manifestCaches.set(manifest, newCache)\n return newCache\n}\n\nfunction getInlineCssForPreparedRoutes(\n manifest: ServerManifest,\n preparedRoutes: PreparedMatchedManifestRoutes,\n) {\n if (preparedRoutes.inlineCss !== undefined) return preparedRoutes.inlineCss\n\n const styles = manifest.inlineCss?.styles\n const hrefs = preparedRoutes.inlineCssHrefs\n if (!styles || !hrefs?.length) return undefined\n\n let css = ''\n for (const href of hrefs) {\n css += styles[href]!\n }\n\n preparedRoutes.inlineCss = css\n return css\n}\n\nfunction getInlineCssAssetForPreparedRoutes(\n manifest: ServerManifest,\n preparedRoutes: PreparedMatchedManifestRoutes,\n) {\n const css = getInlineCssForPreparedRoutes(manifest, preparedRoutes)\n\n return css === undefined ? undefined : createInlineCssStyleAsset(css)\n}\n\nfunction getMatchedRoutesCacheKey(matches: Array<AnyRouteMatch>) {\n let cacheKey = ''\n for (let i = 0; i < matches.length; i++) {\n cacheKey += (i === 0 ? '' : '\\0') + matches[i]!.routeId\n }\n return cacheKey\n}\n\nfunction getPreparedMatchedManifestRoutes(\n manifest: ServerManifest,\n matches: Array<AnyRouteMatch>,\n cacheKey: string,\n) {\n if (isProd) {\n const cached = getManifestCache(manifest).get(cacheKey)\n if (cached) {\n return cached\n }\n }\n\n const preparedRoutes = prepareMatchedManifestRoutes(manifest, matches)\n\n if (isProd) {\n getManifestCache(manifest).set(cacheKey, preparedRoutes)\n }\n\n return preparedRoutes\n}\n\nfunction prepareMatchedManifestRoutes(\n manifest: ServerManifest,\n matches: Array<AnyRouteMatch>,\n): PreparedMatchedManifestRoutes {\n const inlineStyles = manifest.inlineCss?.styles\n const routes: FilteredRoutes = {}\n\n if (!inlineStyles) {\n for (const match of matches) {\n const route = manifest.routes[match.routeId]\n if (route) {\n routes[match.routeId] = route\n }\n }\n return { routes, hasStrippedRoutes: false }\n }\n\n const inlineCssHrefs: Array<string> = []\n const seenInlineCssHrefs = new Set<string>()\n let hasStrippedRoutes = false\n\n for (const match of matches) {\n const routeId = match.routeId\n const route = manifest.routes[routeId]\n if (!route) {\n continue\n }\n\n const nextRoute = stripInlinedStylesheetAssetsFromRoute(\n inlineStyles,\n route,\n inlineCssHrefs,\n seenInlineCssHrefs,\n )\n\n if (nextRoute !== route) {\n hasStrippedRoutes = true\n }\n routes[routeId] = nextRoute\n }\n\n return {\n routes,\n hasStrippedRoutes,\n ...(inlineCssHrefs.length ? { inlineCssHrefs } : {}),\n }\n}\n\nfunction stripInlinedStylesheetAssetsFromRoute(\n inlineStyles: Record<string, string>,\n route: ManifestRoute,\n inlineCssHrefs: Array<string>,\n seenInlineCssHrefs: Set<string>,\n): ManifestRoute {\n const css = route.css\n if (!css) {\n return route\n }\n\n if (css.length === 0) {\n const nextRoute = { ...route }\n delete nextRoute.css\n return nextRoute\n }\n\n let cssLinks: typeof css | undefined\n for (let i = 0; i < css.length; i++) {\n const link = css[i]!\n const href = getStylesheetHref(link)\n if (inlineStyles[href] === undefined) {\n if (cssLinks) {\n cssLinks.push(link)\n }\n continue\n }\n\n if (!seenInlineCssHrefs.has(href)) {\n seenInlineCssHrefs.add(href)\n inlineCssHrefs.push(href)\n }\n\n if (!cssLinks) {\n cssLinks = css.slice(0, i)\n }\n }\n\n if (!cssLinks) {\n return route\n }\n\n if (cssLinks.length > 0) {\n return { ...route, css: cssLinks }\n }\n\n const nextRoute = { ...route }\n delete nextRoute.css\n return nextRoute\n}\n\nfunction hasRouteAssets(route: ManifestRoute) {\n return !!route.scripts?.length || !!route.css?.length\n}\n\nfunction hasRequestAssets(assets: ManifestRouteAssets | undefined) {\n return !!assets && (!!assets.preloads?.length || hasRouteAssets(assets))\n}\n\nfunction mergeRequestAssetsIntoRootRoute(\n rootRoute: ManifestRoute | undefined,\n requestAssets: ManifestRouteAssets | undefined,\n): ManifestRoute {\n const preloads = requestAssets?.preloads?.length\n ? [...requestAssets.preloads, ...(rootRoute?.preloads ?? [])]\n : rootRoute?.preloads\n const scripts = requestAssets?.scripts?.length\n ? [...requestAssets.scripts, ...(rootRoute?.scripts ?? [])]\n : rootRoute?.scripts\n const cssLinks = requestAssets?.css?.length\n ? [...requestAssets.css, ...(rootRoute?.css ?? [])]\n : rootRoute?.css\n\n return {\n ...(rootRoute ?? {}),\n ...(preloads?.length ? { preloads } : {}),\n ...(scripts?.length ? { scripts } : {}),\n ...(cssLinks?.length ? { css: cssLinks } : {}),\n }\n}\n\nexport function attachRouterServerSsrUtils({\n router,\n manifest,\n getRequestAssets,\n}: {\n router: AnyRouter\n manifest: ServerManifest | undefined\n getRequestAssets?: () => ManifestRouteAssets | undefined\n}) {\n router.ssr = {\n get manifest() {\n if (!manifest) return manifest\n\n const requestAssets = getRequestAssets?.()\n const matches = _getRenderedMatches(router.stores.matches.get())\n const hasAssets = hasRequestAssets(requestAssets)\n\n if (!hasAssets && !manifest.inlineCss) {\n return manifest\n }\n\n let inlineCssAsset: Manifest['inlineStyle'] | undefined\n let routes = manifest.routes\n if (manifest.inlineCss) {\n const cacheKey = getMatchedRoutesCacheKey(matches)\n const preparedManifest = getPreparedMatchedManifestRoutes(\n manifest,\n matches,\n cacheKey,\n )\n inlineCssAsset = getInlineCssAssetForPreparedRoutes(\n manifest,\n preparedManifest,\n )\n if (preparedManifest.hasStrippedRoutes) {\n routes = { ...manifest.routes, ...preparedManifest.routes }\n }\n }\n\n if (!hasAssets) {\n return {\n ...(manifest.scriptFormat\n ? { scriptFormat: manifest.scriptFormat }\n : {}),\n ...(inlineCssAsset ? { inlineStyle: inlineCssAsset } : {}),\n routes,\n }\n }\n\n const rootRoute = routes[rootRouteId]\n\n // Merge request-scoped assets into root route without mutating cached manifest\n return {\n ...(manifest.scriptFormat\n ? { scriptFormat: manifest.scriptFormat }\n : {}),\n ...(inlineCssAsset ? { inlineStyle: inlineCssAsset } : {}),\n routes: {\n ...routes,\n [rootRouteId]: mergeRequestAssetsIntoRootRoute(\n rootRoute,\n requestAssets,\n ),\n },\n }\n },\n }\n let _dehydrated = false\n let _serializationFinished = false\n let streamFastPathReserved = false\n const renderFinishedListeners: Array<() => void> = []\n const injectedHtmlListeners: Array<() => void> = []\n const serializationFinishedListeners: Array<() => void> = []\n const cleanupListeners: Array<() => void> = []\n let cleanupStarted = false\n let injectedHtmlBuffer = ''\n\n const callListeners = (listeners: Array<() => void>, errorPrefix: string) => {\n const snapshot = listeners.slice()\n for (const l of snapshot) {\n try {\n l()\n } catch (err) {\n console.error(`${errorPrefix}:`, err)\n }\n }\n }\n\n const removeListener = (\n listeners: Array<() => void>,\n listener: () => void,\n ) => {\n const index = listeners.indexOf(listener)\n if (index >= 0) listeners.splice(index, 1)\n }\n\n const scriptBuffer = new ScriptBuffer((script) => {\n serverSsr.injectScript(script)\n })\n\n const serverSsr: ServerSsr = {\n injectHtml: (html: string) => {\n if (!html || cleanupStarted) return\n // Buffer the HTML so it can be retrieved via takeBufferedHtml()\n injectedHtmlBuffer += html\n callListeners(injectedHtmlListeners, 'SSR injected HTML listener error')\n },\n injectScript: (script: string) => {\n if (!script || cleanupStarted) return\n const html = `<script${router.options.ssr?.nonce ? ` nonce='${router.options.ssr.nonce}'` : ''}>${script}</script>`\n serverSsr.injectHtml(html)\n },\n dehydrate: async (opts?: { requestAssets?: ManifestRouteAssets }) => {\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 = _getRenderedMatches(router.stores.matches.get())\n const isShell = router.isShell()\n if (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 // Only currently matched routes are dehydrated. Other route assets are\n // loaded through dynamic imports when those routes become active.\n if (manifest) {\n const cacheKey = getMatchedRoutesCacheKey(matchesToDehydrate)\n const preparedManifest = getPreparedMatchedManifestRoutes(\n manifest,\n matchesToDehydrate,\n cacheKey,\n )\n\n manifestToDehydrate = {\n ...(manifest.scriptFormat\n ? { scriptFormat: manifest.scriptFormat }\n : {}),\n ...(preparedManifest.inlineCssHrefs\n ? { inlineStyle: createInlineCssPlaceholderAsset() }\n : {}),\n routes: preparedManifest.routes,\n }\n\n // Merge request-scoped assets into root route (without mutating cached manifest)\n const requestAssets = opts?.requestAssets\n if (hasRequestAssets(requestAssets)) {\n const existingRoot = manifestToDehydrate.routes[rootRouteId]\n manifestToDehydrate.routes = {\n ...manifestToDehydrate.routes,\n [rootRouteId]: mergeRequestAssetsIntoRootRoute(\n existingRoot,\n requestAssets,\n ),\n }\n }\n }\n const dehydratedRouter: DehydratedRouter = {\n manifest: manifestToDehydrate,\n matches,\n }\n const dehydratedData = await router.options.dehydrate?.()\n if (cleanupStarted) {\n return\n }\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 let serializationCompleteSignaled = false\n const signalSerializationComplete = () => {\n if (serializationCompleteSignaled || cleanupStarted) return\n serializationCompleteSignaled = true\n _serializationFinished = true\n\n const listeners = serializationFinishedListeners.slice()\n serializationFinishedListeners.length = 0\n\n for (const l of listeners) {\n try {\n l()\n } catch (err) {\n console.error('Serialization listener error:', err)\n }\n }\n }\n\n const finishScriptSerialization = () => {\n if (serializationCompleteSignaled || cleanupStarted) return\n scriptBuffer.enqueue(GLOBAL_TSR + '.e()')\n // Must synchronously notify injected HTML listeners before signaling\n // completion; otherwise the held </body> tail could flush ahead of the\n // end script.\n scriptBuffer.flush()\n signalSerializationComplete()\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 finishScriptSerialization()\n },\n scopeId: SCOPE_ID,\n onDone: () => {\n finishScriptSerialization()\n },\n })\n },\n isDehydrated() {\n return _dehydrated\n },\n isSerializationFinished() {\n return _serializationFinished\n },\n reserveStreamFastPath() {\n if (\n !cleanupStarted &&\n _serializationFinished &&\n !streamFastPathReserved &&\n renderFinishedListeners.length === 0 &&\n !injectedHtmlBuffer &&\n !scriptBuffer.hasPending()\n ) {\n streamFastPathReserved = true\n return true\n }\n return false\n },\n onInjectedHtml: (listener) => {\n if (cleanupStarted) return () => {}\n injectedHtmlListeners.push(listener)\n return () => removeListener(injectedHtmlListeners, listener)\n },\n onRenderFinished: (listener) => {\n if (cleanupStarted || streamFastPathReserved) return\n renderFinishedListeners.push(listener)\n },\n onSerializationFinished: (listener) => {\n if (cleanupStarted) return () => {}\n if (_serializationFinished && !cleanupStarted) {\n try {\n listener()\n } catch (err) {\n console.error('Serialization listener error:', err)\n }\n return () => {}\n }\n serializationFinishedListeners.push(listener)\n return () => removeListener(serializationFinishedListeners, listener)\n },\n onCleanup: (listener) => {\n if (cleanupStarted) return\n cleanupListeners.push(listener)\n },\n setRenderFinished: () => {\n if (cleanupStarted) return\n scriptBuffer.liftBarrier()\n const listeners = renderFinishedListeners.slice()\n renderFinishedListeners.length = 0\n for (const l of listeners) {\n try {\n l()\n } catch (err) {\n console.error('Error in render finished listener:', err)\n }\n }\n if (_serializationFinished) {\n scriptBuffer.flush()\n }\n },\n takeBufferedScripts() {\n const scripts = scriptBuffer.takeAll()\n if (!scripts) return undefined\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/reentrant cleanup calls. A listener could call\n // cleanup() again indirectly; snapshot + clear before invoking so each\n // listener runs exactly once and reentry is a no-op.\n if (cleanupStarted) return\n cleanupStarted = true\n const listeners = cleanupListeners.slice()\n cleanupListeners.length = 0\n for (const l of listeners) {\n try {\n l()\n } catch (err) {\n console.error('Error in SSR cleanup listener:', err)\n }\n }\n renderFinishedListeners.length = 0\n injectedHtmlListeners.length = 0\n serializationFinishedListeners.length = 0\n injectedHtmlBuffer = ''\n scriptBuffer.cleanup()\n router.serverSsr = undefined\n },\n }\n\n router.serverSsr = serverSsr\n for (const listener of router.serverSsrLifecycle?.onServerSsrAttach ?? []) {\n try {\n listener(serverSsr)\n } catch (err) {\n console.error('SSR attach listener error:', err)\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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"}
@@ -2,6 +2,8 @@ import { ReadableStream } from 'node:stream/web';
2
2
  import { Readable } from 'node:stream';
3
3
  import { AnyRouter } from '../router.js';
4
4
  export type TransformStreamWithRouterOptions = {
5
+ /** The request lifetime that owns this response stream. */
6
+ signal?: AbortSignal;
5
7
  /** Timeout for serialization to complete after app render finishes (default: 60000ms) */
6
8
  timeoutMs?: number;
7
9
  /** Maximum lifetime of the stream transform (default: 120000ms). Safety net for cleanup. */