@tanstack/router-core 1.167.1 → 1.167.3

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 (164) hide show
  1. package/dist/cjs/Matches.cjs +15 -12
  2. package/dist/cjs/Matches.cjs.map +1 -1
  3. package/dist/cjs/_virtual/_rolldown/runtime.cjs +23 -0
  4. package/dist/cjs/config.cjs +9 -8
  5. package/dist/cjs/config.cjs.map +1 -1
  6. package/dist/cjs/defer.cjs +37 -21
  7. package/dist/cjs/defer.cjs.map +1 -1
  8. package/dist/cjs/index.cjs +87 -89
  9. package/dist/cjs/isServer/client.cjs +5 -3
  10. package/dist/cjs/isServer/client.cjs.map +1 -1
  11. package/dist/cjs/isServer/development.cjs +5 -3
  12. package/dist/cjs/isServer/development.cjs.map +1 -1
  13. package/dist/cjs/isServer/server.cjs +5 -3
  14. package/dist/cjs/isServer/server.cjs.map +1 -1
  15. package/dist/cjs/link.cjs +5 -4
  16. package/dist/cjs/link.cjs.map +1 -1
  17. package/dist/cjs/load-matches.cjs +622 -766
  18. package/dist/cjs/load-matches.cjs.map +1 -1
  19. package/dist/cjs/lru-cache.cjs +67 -64
  20. package/dist/cjs/lru-cache.cjs.map +1 -1
  21. package/dist/cjs/new-process-route-tree.cjs +707 -792
  22. package/dist/cjs/new-process-route-tree.cjs.map +1 -1
  23. package/dist/cjs/not-found.cjs +20 -7
  24. package/dist/cjs/not-found.cjs.map +1 -1
  25. package/dist/cjs/path.cjs +221 -232
  26. package/dist/cjs/path.cjs.map +1 -1
  27. package/dist/cjs/qss.cjs +62 -28
  28. package/dist/cjs/qss.cjs.map +1 -1
  29. package/dist/cjs/redirect.cjs +44 -30
  30. package/dist/cjs/redirect.cjs.map +1 -1
  31. package/dist/cjs/rewrite.cjs +56 -56
  32. package/dist/cjs/rewrite.cjs.map +1 -1
  33. package/dist/cjs/root.cjs +6 -4
  34. package/dist/cjs/root.cjs.map +1 -1
  35. package/dist/cjs/route.cjs +96 -105
  36. package/dist/cjs/route.cjs.map +1 -1
  37. package/dist/cjs/router.cjs +1154 -1524
  38. package/dist/cjs/router.cjs.map +1 -1
  39. package/dist/cjs/scroll-restoration.cjs +189 -207
  40. package/dist/cjs/scroll-restoration.cjs.map +1 -1
  41. package/dist/cjs/searchMiddleware.cjs +48 -37
  42. package/dist/cjs/searchMiddleware.cjs.map +1 -1
  43. package/dist/cjs/searchParams.cjs +57 -45
  44. package/dist/cjs/searchParams.cjs.map +1 -1
  45. package/dist/cjs/ssr/client.cjs +6 -8
  46. package/dist/cjs/ssr/constants.cjs +6 -5
  47. package/dist/cjs/ssr/constants.cjs.map +1 -1
  48. package/dist/cjs/ssr/createRequestHandler.cjs +41 -59
  49. package/dist/cjs/ssr/createRequestHandler.cjs.map +1 -1
  50. package/dist/cjs/ssr/handlerCallback.cjs +5 -4
  51. package/dist/cjs/ssr/handlerCallback.cjs.map +1 -1
  52. package/dist/cjs/ssr/headers.cjs +17 -26
  53. package/dist/cjs/ssr/headers.cjs.map +1 -1
  54. package/dist/cjs/ssr/json.cjs +8 -4
  55. package/dist/cjs/ssr/json.cjs.map +1 -1
  56. package/dist/cjs/ssr/serializer/RawStream.cjs +268 -268
  57. package/dist/cjs/ssr/serializer/RawStream.cjs.map +1 -1
  58. package/dist/cjs/ssr/serializer/ShallowErrorPlugin.cjs +31 -32
  59. package/dist/cjs/ssr/serializer/ShallowErrorPlugin.cjs.map +1 -1
  60. package/dist/cjs/ssr/serializer/seroval-plugins.cjs +12 -12
  61. package/dist/cjs/ssr/serializer/seroval-plugins.cjs.map +1 -1
  62. package/dist/cjs/ssr/serializer/transformer.cjs +45 -41
  63. package/dist/cjs/ssr/serializer/transformer.cjs.map +1 -1
  64. package/dist/cjs/ssr/server.cjs +12 -14
  65. package/dist/cjs/ssr/ssr-client.cjs +173 -211
  66. package/dist/cjs/ssr/ssr-client.cjs.map +1 -1
  67. package/dist/cjs/ssr/ssr-match-id.cjs +6 -5
  68. package/dist/cjs/ssr/ssr-match-id.cjs.map +1 -1
  69. package/dist/cjs/ssr/ssr-server.cjs +266 -300
  70. package/dist/cjs/ssr/ssr-server.cjs.map +1 -1
  71. package/dist/cjs/ssr/transformStreamWithRouter.cjs +317 -337
  72. package/dist/cjs/ssr/transformStreamWithRouter.cjs.map +1 -1
  73. package/dist/cjs/ssr/tsrScript.cjs +6 -4
  74. package/dist/cjs/ssr/tsrScript.cjs.map +1 -1
  75. package/dist/cjs/utils/batch.cjs +13 -13
  76. package/dist/cjs/utils/batch.cjs.map +1 -1
  77. package/dist/cjs/utils.cjs +274 -208
  78. package/dist/cjs/utils.cjs.map +1 -1
  79. package/dist/esm/Matches.js +16 -13
  80. package/dist/esm/Matches.js.map +1 -1
  81. package/dist/esm/config.js +10 -9
  82. package/dist/esm/config.js.map +1 -1
  83. package/dist/esm/defer.js +37 -22
  84. package/dist/esm/defer.js.map +1 -1
  85. package/dist/esm/index.js +12 -82
  86. package/dist/esm/isServer/client.js +6 -5
  87. package/dist/esm/isServer/client.js.map +1 -1
  88. package/dist/esm/isServer/development.js +6 -5
  89. package/dist/esm/isServer/development.js.map +1 -1
  90. package/dist/esm/isServer/server.js +6 -5
  91. package/dist/esm/isServer/server.js.map +1 -1
  92. package/dist/esm/link.js +6 -5
  93. package/dist/esm/link.js.map +1 -1
  94. package/dist/esm/load-matches.js +617 -765
  95. package/dist/esm/load-matches.js.map +1 -1
  96. package/dist/esm/lru-cache.js +68 -65
  97. package/dist/esm/lru-cache.js.map +1 -1
  98. package/dist/esm/new-process-route-tree.js +705 -797
  99. package/dist/esm/new-process-route-tree.js.map +1 -1
  100. package/dist/esm/not-found.js +21 -9
  101. package/dist/esm/not-found.js.map +1 -1
  102. package/dist/esm/path.js +220 -241
  103. package/dist/esm/path.js.map +1 -1
  104. package/dist/esm/qss.js +63 -30
  105. package/dist/esm/qss.js.map +1 -1
  106. package/dist/esm/redirect.js +45 -34
  107. package/dist/esm/redirect.js.map +1 -1
  108. package/dist/esm/rewrite.js +57 -60
  109. package/dist/esm/rewrite.js.map +1 -1
  110. package/dist/esm/root.js +7 -5
  111. package/dist/esm/root.js.map +1 -1
  112. package/dist/esm/route.js +92 -105
  113. package/dist/esm/route.js.map +1 -1
  114. package/dist/esm/router.js +1148 -1527
  115. package/dist/esm/router.js.map +1 -1
  116. package/dist/esm/scroll-restoration.js +188 -213
  117. package/dist/esm/scroll-restoration.js.map +1 -1
  118. package/dist/esm/searchMiddleware.js +48 -38
  119. package/dist/esm/searchMiddleware.js.map +1 -1
  120. package/dist/esm/searchParams.js +57 -48
  121. package/dist/esm/searchParams.js.map +1 -1
  122. package/dist/esm/ssr/client.js +1 -6
  123. package/dist/esm/ssr/constants.js +7 -7
  124. package/dist/esm/ssr/constants.js.map +1 -1
  125. package/dist/esm/ssr/createRequestHandler.js +39 -58
  126. package/dist/esm/ssr/createRequestHandler.js.map +1 -1
  127. package/dist/esm/ssr/handlerCallback.js +6 -5
  128. package/dist/esm/ssr/handlerCallback.js.map +1 -1
  129. package/dist/esm/ssr/headers.js +16 -26
  130. package/dist/esm/ssr/headers.js.map +1 -1
  131. package/dist/esm/ssr/json.js +9 -5
  132. package/dist/esm/ssr/json.js.map +1 -1
  133. package/dist/esm/ssr/serializer/RawStream.js +267 -273
  134. package/dist/esm/ssr/serializer/RawStream.js.map +1 -1
  135. package/dist/esm/ssr/serializer/ShallowErrorPlugin.js +31 -32
  136. package/dist/esm/ssr/serializer/ShallowErrorPlugin.js.map +1 -1
  137. package/dist/esm/ssr/serializer/seroval-plugins.js +10 -11
  138. package/dist/esm/ssr/serializer/seroval-plugins.js.map +1 -1
  139. package/dist/esm/ssr/serializer/transformer.js +44 -43
  140. package/dist/esm/ssr/serializer/transformer.js.map +1 -1
  141. package/dist/esm/ssr/server.js +2 -12
  142. package/dist/esm/ssr/ssr-client.js +169 -209
  143. package/dist/esm/ssr/ssr-client.js.map +1 -1
  144. package/dist/esm/ssr/ssr-match-id.js +7 -7
  145. package/dist/esm/ssr/ssr-match-id.js.map +1 -1
  146. package/dist/esm/ssr/ssr-server.js +262 -300
  147. package/dist/esm/ssr/ssr-server.js.map +1 -1
  148. package/dist/esm/ssr/transformStreamWithRouter.js +315 -338
  149. package/dist/esm/ssr/transformStreamWithRouter.js.map +1 -1
  150. package/dist/esm/ssr/tsrScript.js +6 -5
  151. package/dist/esm/ssr/tsrScript.js.map +1 -1
  152. package/dist/esm/utils/batch.js +13 -14
  153. package/dist/esm/utils/batch.js.map +1 -1
  154. package/dist/esm/utils.js +273 -224
  155. package/dist/esm/utils.js.map +1 -1
  156. package/package.json +2 -2
  157. package/src/load-matches.ts +4 -1
  158. package/src/router.ts +2 -1
  159. package/dist/cjs/index.cjs.map +0 -1
  160. package/dist/cjs/ssr/client.cjs.map +0 -1
  161. package/dist/cjs/ssr/server.cjs.map +0 -1
  162. package/dist/esm/index.js.map +0 -1
  163. package/dist/esm/ssr/client.js.map +0 -1
  164. package/dist/esm/ssr/server.js.map +0 -1
@@ -1,348 +1,328 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const web = require("node:stream/web");
4
- const node_stream = require("node:stream");
5
- const constants = require("./constants.cjs");
1
+ require("../_virtual/_rolldown/runtime.cjs");
2
+ require("./constants.cjs");
3
+ let node_stream_web = require("node:stream/web");
4
+ let node_stream = require("node:stream");
5
+ //#region src/ssr/transformStreamWithRouter.ts
6
6
  function transformReadableStreamWithRouter(router, routerStream) {
7
- return transformStreamWithRouter(router, routerStream);
7
+ return transformStreamWithRouter(router, routerStream);
8
8
  }
9
9
  function transformPipeableStreamWithRouter(router, routerStream) {
10
- return node_stream.Readable.fromWeb(
11
- transformStreamWithRouter(router, node_stream.Readable.toWeb(routerStream))
12
- );
10
+ return node_stream.Readable.fromWeb(transformStreamWithRouter(router, node_stream.Readable.toWeb(routerStream)));
13
11
  }
14
- const BODY_END_TAG = "</body>";
15
- const HTML_END_TAG = "</html>";
16
- const MIN_CLOSING_TAG_LENGTH = 4;
17
- const DEFAULT_SERIALIZATION_TIMEOUT_MS = 6e4;
18
- const DEFAULT_LIFETIME_TIMEOUT_MS = 6e4;
19
- const textEncoder = new TextEncoder();
12
+ var BODY_END_TAG = "</body>";
13
+ var HTML_END_TAG = "</html>";
14
+ var MIN_CLOSING_TAG_LENGTH = 4;
15
+ var DEFAULT_SERIALIZATION_TIMEOUT_MS = 6e4;
16
+ var DEFAULT_LIFETIME_TIMEOUT_MS = 6e4;
17
+ var textEncoder = new TextEncoder();
18
+ /**
19
+ * Finds the position just after the last valid HTML closing tag in the string.
20
+ *
21
+ * Valid closing tags match the pattern: </[a-zA-Z][\w:.-]*>
22
+ * Examples: </div>, </my-component>, </slot:name.nested>
23
+ *
24
+ * @returns Position after the last closing tag, or -1 if none found
25
+ */
20
26
  function findLastClosingTagEnd(str) {
21
- const len = str.length;
22
- if (len < MIN_CLOSING_TAG_LENGTH) return -1;
23
- let i = len - 1;
24
- while (i >= MIN_CLOSING_TAG_LENGTH - 1) {
25
- if (str.charCodeAt(i) === 62) {
26
- let j = i - 1;
27
- while (j >= 1) {
28
- const code = str.charCodeAt(j);
29
- if (code >= 97 && code <= 122 || // a-z
30
- code >= 65 && code <= 90 || // A-Z
31
- code >= 48 && code <= 57 || // 0-9
32
- code === 95 || // _
33
- code === 58 || // :
34
- code === 46 || // .
35
- code === 45) {
36
- j--;
37
- } else {
38
- break;
39
- }
40
- }
41
- const tagNameStart = j + 1;
42
- if (tagNameStart < i) {
43
- const startCode = str.charCodeAt(tagNameStart);
44
- if (startCode >= 97 && startCode <= 122 || startCode >= 65 && startCode <= 90) {
45
- if (j >= 1 && str.charCodeAt(j) === 47 && str.charCodeAt(j - 1) === 60) {
46
- return i + 1;
47
- }
48
- }
49
- }
50
- }
51
- i--;
52
- }
53
- return -1;
27
+ const len = str.length;
28
+ if (len < MIN_CLOSING_TAG_LENGTH) return -1;
29
+ let i = len - 1;
30
+ while (i >= MIN_CLOSING_TAG_LENGTH - 1) {
31
+ if (str.charCodeAt(i) === 62) {
32
+ let j = i - 1;
33
+ while (j >= 1) {
34
+ const code = str.charCodeAt(j);
35
+ if (code >= 97 && code <= 122 || code >= 65 && code <= 90 || code >= 48 && code <= 57 || code === 95 || code === 58 || code === 46 || code === 45) j--;
36
+ else break;
37
+ }
38
+ const tagNameStart = j + 1;
39
+ if (tagNameStart < i) {
40
+ const startCode = str.charCodeAt(tagNameStart);
41
+ if (startCode >= 97 && startCode <= 122 || startCode >= 65 && startCode <= 90) {
42
+ if (j >= 1 && str.charCodeAt(j) === 47 && str.charCodeAt(j - 1) === 60) return i + 1;
43
+ }
44
+ }
45
+ }
46
+ i--;
47
+ }
48
+ return -1;
54
49
  }
55
50
  function transformStreamWithRouter(router, appStream, opts) {
56
- const serializationAlreadyFinished = router.serverSsr?.isSerializationFinished() ?? false;
57
- const initialBufferedHtml = router.serverSsr?.takeBufferedHtml();
58
- if (serializationAlreadyFinished && !initialBufferedHtml) {
59
- let cleanedUp2 = false;
60
- let controller2;
61
- let isStreamClosed2 = false;
62
- let lifetimeTimeoutHandle2;
63
- const cleanup2 = () => {
64
- if (cleanedUp2) return;
65
- cleanedUp2 = true;
66
- if (lifetimeTimeoutHandle2 !== void 0) {
67
- clearTimeout(lifetimeTimeoutHandle2);
68
- lifetimeTimeoutHandle2 = void 0;
69
- }
70
- router.serverSsr?.cleanup();
71
- };
72
- const safeClose2 = () => {
73
- if (isStreamClosed2) return;
74
- isStreamClosed2 = true;
75
- try {
76
- controller2?.close();
77
- } catch {
78
- }
79
- };
80
- const safeError2 = (error) => {
81
- if (isStreamClosed2) return;
82
- isStreamClosed2 = true;
83
- try {
84
- controller2?.error(error);
85
- } catch {
86
- }
87
- };
88
- const lifetimeMs2 = opts?.lifetimeMs ?? DEFAULT_LIFETIME_TIMEOUT_MS;
89
- lifetimeTimeoutHandle2 = setTimeout(() => {
90
- if (!cleanedUp2 && !isStreamClosed2) {
91
- console.warn(
92
- `SSR stream transform exceeded maximum lifetime (${lifetimeMs2}ms), forcing cleanup`
93
- );
94
- safeError2(new Error("Stream lifetime exceeded"));
95
- cleanup2();
96
- }
97
- }, lifetimeMs2);
98
- const stream2 = new web.ReadableStream({
99
- start(c) {
100
- controller2 = c;
101
- },
102
- cancel() {
103
- isStreamClosed2 = true;
104
- cleanup2();
105
- }
106
- });
107
- (async () => {
108
- const reader = appStream.getReader();
109
- try {
110
- while (true) {
111
- const { done, value } = await reader.read();
112
- if (done) break;
113
- if (cleanedUp2 || isStreamClosed2) return;
114
- controller2?.enqueue(value);
115
- }
116
- if (cleanedUp2 || isStreamClosed2) return;
117
- router.serverSsr?.setRenderFinished();
118
- safeClose2();
119
- cleanup2();
120
- } catch (error) {
121
- if (cleanedUp2) return;
122
- console.error("Error reading appStream:", error);
123
- router.serverSsr?.setRenderFinished();
124
- safeError2(error);
125
- cleanup2();
126
- } finally {
127
- reader.releaseLock();
128
- }
129
- })().catch((error) => {
130
- if (cleanedUp2) return;
131
- console.error("Error in stream transform:", error);
132
- safeError2(error);
133
- cleanup2();
134
- });
135
- return stream2;
136
- }
137
- let stopListeningToInjectedHtml;
138
- let stopListeningToSerializationFinished;
139
- let serializationTimeoutHandle;
140
- let lifetimeTimeoutHandle;
141
- let cleanedUp = false;
142
- let controller;
143
- let isStreamClosed = false;
144
- const textDecoder = new TextDecoder();
145
- let pendingRouterHtml = initialBufferedHtml ?? "";
146
- let leftover = "";
147
- let pendingClosingTags = "";
148
- const MAX_LEFTOVER_CHARS = 2048;
149
- let isAppRendering = true;
150
- let streamBarrierLifted = false;
151
- let serializationFinished = serializationAlreadyFinished;
152
- function safeEnqueue(chunk) {
153
- if (isStreamClosed) return;
154
- if (typeof chunk === "string") {
155
- controller.enqueue(textEncoder.encode(chunk));
156
- } else {
157
- controller.enqueue(chunk);
158
- }
159
- }
160
- function safeClose() {
161
- if (isStreamClosed) return;
162
- isStreamClosed = true;
163
- try {
164
- controller.close();
165
- } catch {
166
- }
167
- }
168
- function safeError(error) {
169
- if (isStreamClosed) return;
170
- isStreamClosed = true;
171
- try {
172
- controller.error(error);
173
- } catch {
174
- }
175
- }
176
- function cleanup() {
177
- if (cleanedUp) return;
178
- cleanedUp = true;
179
- try {
180
- stopListeningToInjectedHtml?.();
181
- stopListeningToSerializationFinished?.();
182
- } catch {
183
- }
184
- stopListeningToInjectedHtml = void 0;
185
- stopListeningToSerializationFinished = void 0;
186
- if (serializationTimeoutHandle !== void 0) {
187
- clearTimeout(serializationTimeoutHandle);
188
- serializationTimeoutHandle = void 0;
189
- }
190
- if (lifetimeTimeoutHandle !== void 0) {
191
- clearTimeout(lifetimeTimeoutHandle);
192
- lifetimeTimeoutHandle = void 0;
193
- }
194
- pendingRouterHtml = "";
195
- leftover = "";
196
- pendingClosingTags = "";
197
- router.serverSsr?.cleanup();
198
- }
199
- const stream = new web.ReadableStream({
200
- start(c) {
201
- controller = c;
202
- },
203
- cancel() {
204
- isStreamClosed = true;
205
- cleanup();
206
- }
207
- });
208
- function flushPendingRouterHtml() {
209
- if (!pendingRouterHtml) return;
210
- safeEnqueue(pendingRouterHtml);
211
- pendingRouterHtml = "";
212
- }
213
- function appendRouterHtml(html) {
214
- if (!html) return;
215
- pendingRouterHtml += html;
216
- }
217
- function tryFinish() {
218
- if (isAppRendering || !serializationFinished) return;
219
- if (cleanedUp || isStreamClosed) return;
220
- if (serializationTimeoutHandle !== void 0) {
221
- clearTimeout(serializationTimeoutHandle);
222
- serializationTimeoutHandle = void 0;
223
- }
224
- const decoderRemainder = textDecoder.decode();
225
- if (leftover) safeEnqueue(leftover);
226
- if (decoderRemainder) safeEnqueue(decoderRemainder);
227
- flushPendingRouterHtml();
228
- if (pendingClosingTags) safeEnqueue(pendingClosingTags);
229
- safeClose();
230
- cleanup();
231
- }
232
- const lifetimeMs = opts?.lifetimeMs ?? DEFAULT_LIFETIME_TIMEOUT_MS;
233
- lifetimeTimeoutHandle = setTimeout(() => {
234
- if (!cleanedUp && !isStreamClosed) {
235
- console.warn(
236
- `SSR stream transform exceeded maximum lifetime (${lifetimeMs}ms), forcing cleanup`
237
- );
238
- safeError(new Error("Stream lifetime exceeded"));
239
- cleanup();
240
- }
241
- }, lifetimeMs);
242
- if (!serializationAlreadyFinished) {
243
- stopListeningToInjectedHtml = router.subscribe("onInjectedHtml", () => {
244
- if (cleanedUp || isStreamClosed) return;
245
- const html = router.serverSsr?.takeBufferedHtml();
246
- if (!html) return;
247
- if (isAppRendering || leftover || pendingClosingTags) {
248
- appendRouterHtml(html);
249
- } else {
250
- safeEnqueue(html);
251
- }
252
- });
253
- stopListeningToSerializationFinished = router.subscribe(
254
- "onSerializationFinished",
255
- () => {
256
- serializationFinished = true;
257
- tryFinish();
258
- }
259
- );
260
- }
261
- (async () => {
262
- const reader = appStream.getReader();
263
- try {
264
- while (true) {
265
- const { done, value } = await reader.read();
266
- if (done) break;
267
- if (cleanedUp || isStreamClosed) return;
268
- const text = value instanceof Uint8Array ? textDecoder.decode(value, { stream: true }) : String(value);
269
- const chunkString = leftover ? leftover + text : text;
270
- if (!streamBarrierLifted) {
271
- if (chunkString.includes(constants.TSR_SCRIPT_BARRIER_ID)) {
272
- streamBarrierLifted = true;
273
- router.serverSsr?.liftScriptBarrier();
274
- }
275
- }
276
- if (pendingClosingTags) {
277
- pendingClosingTags += chunkString;
278
- leftover = "";
279
- continue;
280
- }
281
- const bodyEndIndex = chunkString.indexOf(BODY_END_TAG);
282
- const htmlEndIndex = chunkString.indexOf(HTML_END_TAG);
283
- if (bodyEndIndex !== -1 && htmlEndIndex !== -1 && bodyEndIndex < htmlEndIndex) {
284
- pendingClosingTags = chunkString.slice(bodyEndIndex);
285
- safeEnqueue(chunkString.slice(0, bodyEndIndex));
286
- flushPendingRouterHtml();
287
- leftover = "";
288
- continue;
289
- }
290
- const lastClosingTagEnd = findLastClosingTagEnd(chunkString);
291
- if (lastClosingTagEnd > 0) {
292
- safeEnqueue(chunkString.slice(0, lastClosingTagEnd));
293
- flushPendingRouterHtml();
294
- leftover = chunkString.slice(lastClosingTagEnd);
295
- if (leftover.length > MAX_LEFTOVER_CHARS) {
296
- safeEnqueue(leftover.slice(0, leftover.length - MAX_LEFTOVER_CHARS));
297
- leftover = leftover.slice(-MAX_LEFTOVER_CHARS);
298
- }
299
- } else {
300
- const combined = chunkString;
301
- if (combined.length > MAX_LEFTOVER_CHARS) {
302
- const flushUpto = combined.length - MAX_LEFTOVER_CHARS;
303
- safeEnqueue(combined.slice(0, flushUpto));
304
- leftover = combined.slice(flushUpto);
305
- } else {
306
- leftover = combined;
307
- }
308
- }
309
- }
310
- if (cleanedUp || isStreamClosed) return;
311
- isAppRendering = false;
312
- router.serverSsr?.setRenderFinished();
313
- if (serializationFinished) {
314
- tryFinish();
315
- } else {
316
- const timeoutMs = opts?.timeoutMs ?? DEFAULT_SERIALIZATION_TIMEOUT_MS;
317
- serializationTimeoutHandle = setTimeout(() => {
318
- if (!cleanedUp && !isStreamClosed) {
319
- console.error("Serialization timeout after app render finished");
320
- safeError(
321
- new Error("Serialization timeout after app render finished")
322
- );
323
- cleanup();
324
- }
325
- }, timeoutMs);
326
- }
327
- } catch (error) {
328
- if (cleanedUp) return;
329
- console.error("Error reading appStream:", error);
330
- isAppRendering = false;
331
- router.serverSsr?.setRenderFinished();
332
- safeError(error);
333
- cleanup();
334
- } finally {
335
- reader.releaseLock();
336
- }
337
- })().catch((error) => {
338
- if (cleanedUp) return;
339
- console.error("Error in stream transform:", error);
340
- safeError(error);
341
- cleanup();
342
- });
343
- return stream;
51
+ const serializationAlreadyFinished = router.serverSsr?.isSerializationFinished() ?? false;
52
+ const initialBufferedHtml = router.serverSsr?.takeBufferedHtml();
53
+ if (serializationAlreadyFinished && !initialBufferedHtml) {
54
+ let cleanedUp = false;
55
+ let controller;
56
+ let isStreamClosed = false;
57
+ let lifetimeTimeoutHandle;
58
+ const cleanup = () => {
59
+ if (cleanedUp) return;
60
+ cleanedUp = true;
61
+ if (lifetimeTimeoutHandle !== void 0) {
62
+ clearTimeout(lifetimeTimeoutHandle);
63
+ lifetimeTimeoutHandle = void 0;
64
+ }
65
+ router.serverSsr?.cleanup();
66
+ };
67
+ const safeClose = () => {
68
+ if (isStreamClosed) return;
69
+ isStreamClosed = true;
70
+ try {
71
+ controller?.close();
72
+ } catch {}
73
+ };
74
+ const safeError = (error) => {
75
+ if (isStreamClosed) return;
76
+ isStreamClosed = true;
77
+ try {
78
+ controller?.error(error);
79
+ } catch {}
80
+ };
81
+ const lifetimeMs = opts?.lifetimeMs ?? DEFAULT_LIFETIME_TIMEOUT_MS;
82
+ lifetimeTimeoutHandle = setTimeout(() => {
83
+ if (!cleanedUp && !isStreamClosed) {
84
+ console.warn(`SSR stream transform exceeded maximum lifetime (${lifetimeMs}ms), forcing cleanup`);
85
+ safeError(/* @__PURE__ */ new Error("Stream lifetime exceeded"));
86
+ cleanup();
87
+ }
88
+ }, lifetimeMs);
89
+ const stream = new node_stream_web.ReadableStream({
90
+ start(c) {
91
+ controller = c;
92
+ },
93
+ cancel() {
94
+ isStreamClosed = true;
95
+ cleanup();
96
+ }
97
+ });
98
+ (async () => {
99
+ const reader = appStream.getReader();
100
+ try {
101
+ while (true) {
102
+ const { done, value } = await reader.read();
103
+ if (done) break;
104
+ if (cleanedUp || isStreamClosed) return;
105
+ controller?.enqueue(value);
106
+ }
107
+ if (cleanedUp || isStreamClosed) return;
108
+ router.serverSsr?.setRenderFinished();
109
+ safeClose();
110
+ cleanup();
111
+ } catch (error) {
112
+ if (cleanedUp) return;
113
+ console.error("Error reading appStream:", error);
114
+ router.serverSsr?.setRenderFinished();
115
+ safeError(error);
116
+ cleanup();
117
+ } finally {
118
+ reader.releaseLock();
119
+ }
120
+ })().catch((error) => {
121
+ if (cleanedUp) return;
122
+ console.error("Error in stream transform:", error);
123
+ safeError(error);
124
+ cleanup();
125
+ });
126
+ return stream;
127
+ }
128
+ let stopListeningToInjectedHtml;
129
+ let stopListeningToSerializationFinished;
130
+ let serializationTimeoutHandle;
131
+ let lifetimeTimeoutHandle;
132
+ let cleanedUp = false;
133
+ let controller;
134
+ let isStreamClosed = false;
135
+ const textDecoder = new TextDecoder();
136
+ let pendingRouterHtml = initialBufferedHtml ?? "";
137
+ let leftover = "";
138
+ let pendingClosingTags = "";
139
+ const MAX_LEFTOVER_CHARS = 2048;
140
+ let isAppRendering = true;
141
+ let streamBarrierLifted = false;
142
+ let serializationFinished = serializationAlreadyFinished;
143
+ function safeEnqueue(chunk) {
144
+ if (isStreamClosed) return;
145
+ if (typeof chunk === "string") controller.enqueue(textEncoder.encode(chunk));
146
+ else controller.enqueue(chunk);
147
+ }
148
+ function safeClose() {
149
+ if (isStreamClosed) return;
150
+ isStreamClosed = true;
151
+ try {
152
+ controller.close();
153
+ } catch {}
154
+ }
155
+ function safeError(error) {
156
+ if (isStreamClosed) return;
157
+ isStreamClosed = true;
158
+ try {
159
+ controller.error(error);
160
+ } catch {}
161
+ }
162
+ /**
163
+ * Cleanup with guards; must be idempotent.
164
+ */
165
+ function cleanup() {
166
+ if (cleanedUp) return;
167
+ cleanedUp = true;
168
+ try {
169
+ stopListeningToInjectedHtml?.();
170
+ stopListeningToSerializationFinished?.();
171
+ } catch {}
172
+ stopListeningToInjectedHtml = void 0;
173
+ stopListeningToSerializationFinished = void 0;
174
+ if (serializationTimeoutHandle !== void 0) {
175
+ clearTimeout(serializationTimeoutHandle);
176
+ serializationTimeoutHandle = void 0;
177
+ }
178
+ if (lifetimeTimeoutHandle !== void 0) {
179
+ clearTimeout(lifetimeTimeoutHandle);
180
+ lifetimeTimeoutHandle = void 0;
181
+ }
182
+ pendingRouterHtml = "";
183
+ leftover = "";
184
+ pendingClosingTags = "";
185
+ router.serverSsr?.cleanup();
186
+ }
187
+ const stream = new node_stream_web.ReadableStream({
188
+ start(c) {
189
+ controller = c;
190
+ },
191
+ cancel() {
192
+ isStreamClosed = true;
193
+ cleanup();
194
+ }
195
+ });
196
+ function flushPendingRouterHtml() {
197
+ if (!pendingRouterHtml) return;
198
+ safeEnqueue(pendingRouterHtml);
199
+ pendingRouterHtml = "";
200
+ }
201
+ function appendRouterHtml(html) {
202
+ if (!html) return;
203
+ pendingRouterHtml += html;
204
+ }
205
+ /**
206
+ * Finish only when app done and serialization complete.
207
+ */
208
+ function tryFinish() {
209
+ if (isAppRendering || !serializationFinished) return;
210
+ if (cleanedUp || isStreamClosed) return;
211
+ if (serializationTimeoutHandle !== void 0) {
212
+ clearTimeout(serializationTimeoutHandle);
213
+ serializationTimeoutHandle = void 0;
214
+ }
215
+ const decoderRemainder = textDecoder.decode();
216
+ if (leftover) safeEnqueue(leftover);
217
+ if (decoderRemainder) safeEnqueue(decoderRemainder);
218
+ flushPendingRouterHtml();
219
+ if (pendingClosingTags) safeEnqueue(pendingClosingTags);
220
+ safeClose();
221
+ cleanup();
222
+ }
223
+ const lifetimeMs = opts?.lifetimeMs ?? DEFAULT_LIFETIME_TIMEOUT_MS;
224
+ lifetimeTimeoutHandle = setTimeout(() => {
225
+ if (!cleanedUp && !isStreamClosed) {
226
+ console.warn(`SSR stream transform exceeded maximum lifetime (${lifetimeMs}ms), forcing cleanup`);
227
+ safeError(/* @__PURE__ */ new Error("Stream lifetime exceeded"));
228
+ cleanup();
229
+ }
230
+ }, lifetimeMs);
231
+ if (!serializationAlreadyFinished) {
232
+ stopListeningToInjectedHtml = router.subscribe("onInjectedHtml", () => {
233
+ if (cleanedUp || isStreamClosed) return;
234
+ const html = router.serverSsr?.takeBufferedHtml();
235
+ if (!html) return;
236
+ if (isAppRendering || leftover || pendingClosingTags) appendRouterHtml(html);
237
+ else safeEnqueue(html);
238
+ });
239
+ stopListeningToSerializationFinished = router.subscribe("onSerializationFinished", () => {
240
+ serializationFinished = true;
241
+ tryFinish();
242
+ });
243
+ }
244
+ (async () => {
245
+ const reader = appStream.getReader();
246
+ try {
247
+ while (true) {
248
+ const { done, value } = await reader.read();
249
+ if (done) break;
250
+ if (cleanedUp || isStreamClosed) return;
251
+ const text = value instanceof Uint8Array ? textDecoder.decode(value, { stream: true }) : String(value);
252
+ const chunkString = leftover ? leftover + text : text;
253
+ if (!streamBarrierLifted) {
254
+ if (chunkString.includes("$tsr-stream-barrier")) {
255
+ streamBarrierLifted = true;
256
+ router.serverSsr?.liftScriptBarrier();
257
+ }
258
+ }
259
+ if (pendingClosingTags) {
260
+ pendingClosingTags += chunkString;
261
+ leftover = "";
262
+ continue;
263
+ }
264
+ const bodyEndIndex = chunkString.indexOf(BODY_END_TAG);
265
+ const htmlEndIndex = chunkString.indexOf(HTML_END_TAG);
266
+ if (bodyEndIndex !== -1 && htmlEndIndex !== -1 && bodyEndIndex < htmlEndIndex) {
267
+ pendingClosingTags = chunkString.slice(bodyEndIndex);
268
+ safeEnqueue(chunkString.slice(0, bodyEndIndex));
269
+ flushPendingRouterHtml();
270
+ leftover = "";
271
+ continue;
272
+ }
273
+ const lastClosingTagEnd = findLastClosingTagEnd(chunkString);
274
+ if (lastClosingTagEnd > 0) {
275
+ safeEnqueue(chunkString.slice(0, lastClosingTagEnd));
276
+ flushPendingRouterHtml();
277
+ leftover = chunkString.slice(lastClosingTagEnd);
278
+ if (leftover.length > MAX_LEFTOVER_CHARS) {
279
+ safeEnqueue(leftover.slice(0, leftover.length - MAX_LEFTOVER_CHARS));
280
+ leftover = leftover.slice(-MAX_LEFTOVER_CHARS);
281
+ }
282
+ } else {
283
+ const combined = chunkString;
284
+ if (combined.length > MAX_LEFTOVER_CHARS) {
285
+ const flushUpto = combined.length - MAX_LEFTOVER_CHARS;
286
+ safeEnqueue(combined.slice(0, flushUpto));
287
+ leftover = combined.slice(flushUpto);
288
+ } else leftover = combined;
289
+ }
290
+ }
291
+ if (cleanedUp || isStreamClosed) return;
292
+ isAppRendering = false;
293
+ router.serverSsr?.setRenderFinished();
294
+ if (serializationFinished) tryFinish();
295
+ else {
296
+ const timeoutMs = opts?.timeoutMs ?? DEFAULT_SERIALIZATION_TIMEOUT_MS;
297
+ serializationTimeoutHandle = setTimeout(() => {
298
+ if (!cleanedUp && !isStreamClosed) {
299
+ console.error("Serialization timeout after app render finished");
300
+ safeError(/* @__PURE__ */ new Error("Serialization timeout after app render finished"));
301
+ cleanup();
302
+ }
303
+ }, timeoutMs);
304
+ }
305
+ } catch (error) {
306
+ if (cleanedUp) return;
307
+ console.error("Error reading appStream:", error);
308
+ isAppRendering = false;
309
+ router.serverSsr?.setRenderFinished();
310
+ safeError(error);
311
+ cleanup();
312
+ } finally {
313
+ reader.releaseLock();
314
+ }
315
+ })().catch((error) => {
316
+ if (cleanedUp) return;
317
+ console.error("Error in stream transform:", error);
318
+ safeError(error);
319
+ cleanup();
320
+ });
321
+ return stream;
344
322
  }
323
+ //#endregion
345
324
  exports.transformPipeableStreamWithRouter = transformPipeableStreamWithRouter;
346
325
  exports.transformReadableStreamWithRouter = transformReadableStreamWithRouter;
347
326
  exports.transformStreamWithRouter = transformStreamWithRouter;
348
- //# sourceMappingURL=transformStreamWithRouter.cjs.map
327
+
328
+ //# sourceMappingURL=transformStreamWithRouter.cjs.map