@tanstack/router-core 1.139.11 → 1.139.13

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.
@@ -35,7 +35,7 @@ type ReadablePassthrough = {
35
35
  destroyed: boolean
36
36
  }
37
37
 
38
- function createPassthrough(onCancel?: () => void) {
38
+ function createPassthrough(onCancel: () => void) {
39
39
  let controller: ReadableStreamDefaultController<any>
40
40
  const encoder = new TextEncoder()
41
41
  const stream = new ReadableStream({
@@ -44,13 +44,15 @@ function createPassthrough(onCancel?: () => void) {
44
44
  },
45
45
  cancel() {
46
46
  res.destroyed = true
47
- onCancel?.()
47
+ onCancel()
48
48
  },
49
49
  })
50
50
 
51
51
  const res: ReadablePassthrough = {
52
52
  stream,
53
53
  write: (chunk) => {
54
+ // Don't write to destroyed stream
55
+ if (res.destroyed) return
54
56
  if (typeof chunk === 'string') {
55
57
  controller.enqueue(encoder.encode(chunk))
56
58
  } else {
@@ -58,13 +60,17 @@ function createPassthrough(onCancel?: () => void) {
58
60
  }
59
61
  },
60
62
  end: (chunk) => {
63
+ // Don't end already destroyed stream
64
+ if (res.destroyed) return
61
65
  if (chunk) {
62
66
  res.write(chunk)
63
67
  }
64
- controller.close()
65
68
  res.destroyed = true
69
+ controller.close()
66
70
  },
67
71
  destroy: (error) => {
72
+ // Don't destroy already destroyed stream
73
+ if (res.destroyed) return
68
74
  res.destroyed = true
69
75
  controller.error(error)
70
76
  },
@@ -105,14 +111,20 @@ export function transformStreamWithRouter(
105
111
  ) {
106
112
  let stopListeningToInjectedHtml: (() => void) | undefined = undefined
107
113
  let timeoutHandle: NodeJS.Timeout
114
+ let cleanedUp = false
108
115
 
109
- const finalPassThrough = createPassthrough(() => {
116
+ function cleanup() {
117
+ if (cleanedUp) return
118
+ cleanedUp = true
110
119
  if (stopListeningToInjectedHtml) {
111
120
  stopListeningToInjectedHtml()
112
121
  stopListeningToInjectedHtml = undefined
113
122
  }
114
123
  clearTimeout(timeoutHandle)
115
- })
124
+ router.serverSsr?.cleanup()
125
+ }
126
+
127
+ const finalPassThrough = createPassthrough(cleanup)
116
128
  const textDecoder = new TextDecoder()
117
129
 
118
130
  let isAppRendering = true
@@ -149,13 +161,16 @@ export function transformStreamWithRouter(
149
161
  )
150
162
 
151
163
  function handleInjectedHtml() {
164
+ // Don't process if already cleaned up
165
+ if (cleanedUp) return
166
+
152
167
  router.serverSsr!.injectedHtml.forEach((promise) => {
153
168
  processingCount++
154
169
 
155
170
  promise
156
171
  .then((html) => {
157
- // Don't write to destroyed stream
158
- if (finalPassThrough.destroyed) {
172
+ // Don't write to destroyed stream or after cleanup
173
+ if (cleanedUp || finalPassThrough.destroyed) {
159
174
  return
160
175
  }
161
176
  if (isAppRendering) {
@@ -165,10 +180,7 @@ export function transformStreamWithRouter(
165
180
  }
166
181
  })
167
182
  .catch((err) => {
168
- // Only reject if not already settled
169
- if (!finalPassThrough.destroyed) {
170
- injectedHtmlDonePromise.reject(err)
171
- }
183
+ injectedHtmlDonePromise.reject(err)
172
184
  })
173
185
  .finally(() => {
174
186
  processingCount--
@@ -183,6 +195,11 @@ export function transformStreamWithRouter(
183
195
 
184
196
  injectedHtmlDonePromise
185
197
  .then(() => {
198
+ // Don't process if already cleaned up or destroyed
199
+ if (cleanedUp || finalPassThrough.destroyed) {
200
+ return
201
+ }
202
+
186
203
  clearTimeout(timeoutHandle)
187
204
  const finalHtml =
188
205
  leftover + leftoverHtml + getBufferedRouterStream() + pendingClosingTags
@@ -194,19 +211,24 @@ export function transformStreamWithRouter(
194
211
  finalPassThrough.end(finalHtml)
195
212
  })
196
213
  .catch((err) => {
214
+ // Don't process if already cleaned up
215
+ if (cleanedUp || finalPassThrough.destroyed) {
216
+ return
217
+ }
218
+
197
219
  console.error('Error reading routerStream:', err)
198
220
  finalPassThrough.destroy(err)
199
221
  })
200
- .finally(() => {
201
- if (stopListeningToInjectedHtml) {
202
- stopListeningToInjectedHtml()
203
- stopListeningToInjectedHtml = undefined
204
- }
205
- })
222
+ .finally(cleanup)
206
223
 
207
224
  // Transform the appStream
208
225
  readStream(appStream, {
209
226
  onData: (chunk) => {
227
+ // Don't process if already cleaned up
228
+ if (cleanedUp || finalPassThrough.destroyed) {
229
+ return
230
+ }
231
+
210
232
  const text = decodeChunk(chunk.value)
211
233
  const chunkString = leftover + text
212
234
  const bodyEndMatch = chunkString.match(patternBodyEnd)
@@ -266,8 +288,8 @@ export function transformStreamWithRouter(
266
288
  }
267
289
  },
268
290
  onEnd: () => {
269
- // Don't process if stream was already destroyed/cancelled
270
- if (finalPassThrough.destroyed) {
291
+ // Don't process if stream was already destroyed/cancelled or cleaned up
292
+ if (cleanedUp || finalPassThrough.destroyed) {
271
293
  return
272
294
  }
273
295
 
@@ -288,6 +310,11 @@ export function transformStreamWithRouter(
288
310
  }
289
311
  },
290
312
  onError: (error) => {
313
+ // Don't process if already cleaned up
314
+ if (cleanedUp) {
315
+ return
316
+ }
317
+
291
318
  console.error('Error reading appStream:', error)
292
319
  isAppRendering = false
293
320
  router.serverSsr!.setRenderFinished()