@tanstack/router-core 1.136.6 → 1.136.9
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.
- package/dist/cjs/new-process-route-tree.cjs +17 -3
- package/dist/cjs/new-process-route-tree.cjs.map +1 -1
- package/dist/cjs/new-process-route-tree.d.cts +9 -5
- package/dist/cjs/router.cjs +5 -11
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/router.d.cts +5 -4
- package/dist/cjs/ssr/ssr-server.cjs +26 -2
- package/dist/cjs/ssr/ssr-server.cjs.map +1 -1
- package/dist/cjs/ssr/transformStreamWithRouter.cjs +32 -35
- package/dist/cjs/ssr/transformStreamWithRouter.cjs.map +1 -1
- package/dist/cjs/ssr/transformStreamWithRouter.d.cts +4 -1
- package/dist/esm/new-process-route-tree.d.ts +9 -5
- package/dist/esm/new-process-route-tree.js +17 -3
- package/dist/esm/new-process-route-tree.js.map +1 -1
- package/dist/esm/router.d.ts +5 -4
- package/dist/esm/router.js +5 -11
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/ssr/ssr-server.js +26 -2
- package/dist/esm/ssr/ssr-server.js.map +1 -1
- package/dist/esm/ssr/transformStreamWithRouter.d.ts +4 -1
- package/dist/esm/ssr/transformStreamWithRouter.js +32 -35
- package/dist/esm/ssr/transformStreamWithRouter.js.map +1 -1
- package/package.json +1 -1
- package/src/new-process-route-tree.ts +27 -9
- package/src/router.ts +9 -17
- package/src/ssr/ssr-server.ts +29 -4
- package/src/ssr/transformStreamWithRouter.ts +35 -39
|
@@ -19,19 +19,17 @@ export function transformPipeableStreamWithRouter(
|
|
|
19
19
|
)
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
export const TSR_SCRIPT_BARRIER_ID = '$tsr-stream-barrier'
|
|
23
|
+
|
|
22
24
|
// regex pattern for matching closing body and html tags
|
|
23
|
-
const patternBodyStart = /(<body)/
|
|
24
25
|
const patternBodyEnd = /(<\/body>)/
|
|
25
26
|
const patternHtmlEnd = /(<\/html>)/
|
|
26
|
-
const patternHeadStart = /(<head.*?>)/
|
|
27
27
|
// regex pattern for matching closing tags
|
|
28
28
|
const patternClosingTag = /(<\/[a-zA-Z][\w:.-]*?>)/g
|
|
29
29
|
|
|
30
|
-
const textDecoder = new TextDecoder()
|
|
31
|
-
|
|
32
30
|
type ReadablePassthrough = {
|
|
33
31
|
stream: ReadableStream
|
|
34
|
-
write: (chunk:
|
|
32
|
+
write: (chunk: unknown) => void
|
|
35
33
|
end: (chunk?: string) => void
|
|
36
34
|
destroy: (error: unknown) => void
|
|
37
35
|
destroyed: boolean
|
|
@@ -49,11 +47,15 @@ function createPassthrough() {
|
|
|
49
47
|
const res: ReadablePassthrough = {
|
|
50
48
|
stream,
|
|
51
49
|
write: (chunk) => {
|
|
52
|
-
|
|
50
|
+
if (typeof chunk === 'string') {
|
|
51
|
+
controller.enqueue(encoder.encode(chunk))
|
|
52
|
+
} else {
|
|
53
|
+
controller.enqueue(chunk)
|
|
54
|
+
}
|
|
53
55
|
},
|
|
54
56
|
end: (chunk) => {
|
|
55
57
|
if (chunk) {
|
|
56
|
-
|
|
58
|
+
res.write(chunk)
|
|
57
59
|
}
|
|
58
60
|
controller.close()
|
|
59
61
|
res.destroyed = true
|
|
@@ -90,16 +92,20 @@ async function readStream(
|
|
|
90
92
|
export function transformStreamWithRouter(
|
|
91
93
|
router: AnyRouter,
|
|
92
94
|
appStream: ReadableStream,
|
|
95
|
+
opts?: {
|
|
96
|
+
timeoutMs?: number
|
|
97
|
+
},
|
|
93
98
|
) {
|
|
94
99
|
const finalPassThrough = createPassthrough()
|
|
100
|
+
const textDecoder = new TextDecoder()
|
|
95
101
|
|
|
96
102
|
let isAppRendering = true as boolean
|
|
97
103
|
let routerStreamBuffer = ''
|
|
98
104
|
let pendingClosingTags = ''
|
|
99
|
-
let
|
|
100
|
-
let headStarted = false as boolean
|
|
105
|
+
let streamBarrierLifted = false as boolean
|
|
101
106
|
let leftover = ''
|
|
102
107
|
let leftoverHtml = ''
|
|
108
|
+
let timeoutHandle: NodeJS.Timeout
|
|
103
109
|
|
|
104
110
|
function getBufferedRouterStream() {
|
|
105
111
|
const html = routerStreamBuffer
|
|
@@ -109,7 +115,7 @@ export function transformStreamWithRouter(
|
|
|
109
115
|
|
|
110
116
|
function decodeChunk(chunk: unknown): string {
|
|
111
117
|
if (chunk instanceof Uint8Array) {
|
|
112
|
-
return textDecoder.decode(chunk)
|
|
118
|
+
return textDecoder.decode(chunk, { stream: true })
|
|
113
119
|
}
|
|
114
120
|
return String(chunk)
|
|
115
121
|
}
|
|
@@ -136,7 +142,7 @@ export function transformStreamWithRouter(
|
|
|
136
142
|
|
|
137
143
|
promise
|
|
138
144
|
.then((html) => {
|
|
139
|
-
if (
|
|
145
|
+
if (isAppRendering) {
|
|
140
146
|
routerStreamBuffer += html
|
|
141
147
|
} else {
|
|
142
148
|
finalPassThrough.write(html)
|
|
@@ -147,7 +153,6 @@ export function transformStreamWithRouter(
|
|
|
147
153
|
processingCount--
|
|
148
154
|
|
|
149
155
|
if (!isAppRendering && processingCount === 0) {
|
|
150
|
-
stopListeningToInjectedHtml()
|
|
151
156
|
injectedHtmlDonePromise.resolve()
|
|
152
157
|
}
|
|
153
158
|
})
|
|
@@ -155,6 +160,7 @@ export function transformStreamWithRouter(
|
|
|
155
160
|
|
|
156
161
|
injectedHtmlDonePromise
|
|
157
162
|
.then(() => {
|
|
163
|
+
clearTimeout(timeoutHandle)
|
|
158
164
|
const finalHtml =
|
|
159
165
|
leftoverHtml + getBufferedRouterStream() + pendingClosingTags
|
|
160
166
|
|
|
@@ -164,44 +170,26 @@ export function transformStreamWithRouter(
|
|
|
164
170
|
console.error('Error reading routerStream:', err)
|
|
165
171
|
finalPassThrough.destroy(err)
|
|
166
172
|
})
|
|
173
|
+
.finally(stopListeningToInjectedHtml)
|
|
167
174
|
|
|
168
175
|
// Transform the appStream
|
|
169
176
|
readStream(appStream, {
|
|
170
177
|
onData: (chunk) => {
|
|
171
178
|
const text = decodeChunk(chunk.value)
|
|
172
|
-
|
|
173
|
-
let chunkString = leftover + text
|
|
179
|
+
const chunkString = leftover + text
|
|
174
180
|
const bodyEndMatch = chunkString.match(patternBodyEnd)
|
|
175
181
|
const htmlEndMatch = chunkString.match(patternHtmlEnd)
|
|
176
182
|
|
|
177
|
-
if (!
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
if (!headStarted) {
|
|
185
|
-
const headStartMatch = chunkString.match(patternHeadStart)
|
|
186
|
-
if (headStartMatch) {
|
|
187
|
-
headStarted = true
|
|
188
|
-
const index = headStartMatch.index!
|
|
189
|
-
const headTag = headStartMatch[0]
|
|
190
|
-
const remaining = chunkString.slice(index + headTag.length)
|
|
191
|
-
finalPassThrough.write(
|
|
192
|
-
chunkString.slice(0, index) + headTag + getBufferedRouterStream(),
|
|
193
|
-
)
|
|
194
|
-
// make sure to only write `remaining` until the next closing tag
|
|
195
|
-
chunkString = remaining
|
|
183
|
+
if (!streamBarrierLifted) {
|
|
184
|
+
const streamBarrierIdIncluded = chunkString.includes(
|
|
185
|
+
TSR_SCRIPT_BARRIER_ID,
|
|
186
|
+
)
|
|
187
|
+
if (streamBarrierIdIncluded) {
|
|
188
|
+
streamBarrierLifted = true
|
|
189
|
+
router.serverSsr!.liftScriptBarrier()
|
|
196
190
|
}
|
|
197
191
|
}
|
|
198
192
|
|
|
199
|
-
if (!bodyStarted) {
|
|
200
|
-
finalPassThrough.write(chunkString)
|
|
201
|
-
leftover = ''
|
|
202
|
-
return
|
|
203
|
-
}
|
|
204
|
-
|
|
205
193
|
// If either the body end or html end is in the chunk,
|
|
206
194
|
// We need to get all of our data in asap
|
|
207
195
|
if (
|
|
@@ -247,11 +235,19 @@ export function transformStreamWithRouter(
|
|
|
247
235
|
// If there are no pending promises, resolve the injectedHtmlDonePromise
|
|
248
236
|
if (processingCount === 0) {
|
|
249
237
|
injectedHtmlDonePromise.resolve()
|
|
238
|
+
} else {
|
|
239
|
+
const timeoutMs = opts?.timeoutMs ?? 60000
|
|
240
|
+
timeoutHandle = setTimeout(() => {
|
|
241
|
+
injectedHtmlDonePromise.reject(
|
|
242
|
+
new Error('Injected HTML timeout after app render finished'),
|
|
243
|
+
)
|
|
244
|
+
}, timeoutMs)
|
|
250
245
|
}
|
|
251
246
|
},
|
|
252
247
|
onError: (error) => {
|
|
253
248
|
console.error('Error reading appStream:', error)
|
|
254
249
|
finalPassThrough.destroy(error)
|
|
250
|
+
injectedHtmlDonePromise.reject(error)
|
|
255
251
|
},
|
|
256
252
|
})
|
|
257
253
|
|