@tanstack/router-core 1.171.14 → 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.
- package/dist/cjs/Matches.cjs.map +1 -1
- package/dist/cjs/Matches.d.cts +3 -12
- package/dist/cjs/await-signal.cjs +21 -0
- package/dist/cjs/await-signal.cjs.map +1 -0
- package/dist/cjs/await-signal.d.cts +1 -0
- package/dist/cjs/index.cjs +3 -1
- package/dist/cjs/index.d.cts +3 -2
- package/dist/cjs/isServer/client.cjs +2 -0
- package/dist/cjs/isServer/client.cjs.map +1 -1
- package/dist/cjs/isServer/client.d.cts +1 -0
- package/dist/cjs/isServer/development.cjs +2 -0
- package/dist/cjs/isServer/development.cjs.map +1 -1
- package/dist/cjs/isServer/development.d.cts +1 -0
- package/dist/cjs/isServer/server.cjs +2 -0
- package/dist/cjs/isServer/server.cjs.map +1 -1
- package/dist/cjs/isServer/server.d.cts +1 -0
- package/dist/cjs/load-client.cjs +1470 -0
- package/dist/cjs/load-client.cjs.map +1 -0
- package/dist/cjs/load-client.d.cts +147 -0
- package/dist/cjs/load-server.cjs +565 -0
- package/dist/cjs/load-server.cjs.map +1 -0
- package/dist/cjs/load-server.d.cts +16 -0
- package/dist/cjs/redirect.cjs.map +1 -1
- package/dist/cjs/redirect.d.cts +0 -1
- package/dist/cjs/route.cjs.map +1 -1
- package/dist/cjs/router.cjs +187 -445
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/router.d.cts +62 -52
- package/dist/cjs/scroll-restoration.cjs +27 -34
- package/dist/cjs/scroll-restoration.cjs.map +1 -1
- package/dist/cjs/ssr/client.cjs +2 -2
- package/dist/cjs/ssr/createRequestHandler.cjs +60 -9
- package/dist/cjs/ssr/createRequestHandler.cjs.map +1 -1
- package/dist/cjs/ssr/createRequestHandler.d.cts +1 -0
- package/dist/cjs/ssr/handlerCallback.cjs +49 -4
- package/dist/cjs/ssr/handlerCallback.cjs.map +1 -1
- package/dist/cjs/ssr/handlerCallback.d.cts +3 -0
- package/dist/cjs/ssr/server.cjs +4 -0
- package/dist/cjs/ssr/server.d.cts +2 -2
- package/dist/cjs/ssr/ssr-client.d.cts +1 -10
- package/dist/cjs/ssr/ssr-match-id.cjs +2 -2
- package/dist/cjs/ssr/ssr-match-id.cjs.map +1 -1
- package/dist/cjs/ssr/ssr-server.cjs +6 -6
- package/dist/cjs/ssr/ssr-server.cjs.map +1 -1
- package/dist/cjs/ssr/transformStreamWithRouter.cjs +27 -1
- package/dist/cjs/ssr/transformStreamWithRouter.cjs.map +1 -1
- package/dist/cjs/ssr/transformStreamWithRouter.d.cts +2 -0
- package/dist/cjs/ssr/types.d.cts +0 -1
- package/dist/cjs/stores.cjs +30 -104
- package/dist/cjs/stores.cjs.map +1 -1
- package/dist/cjs/stores.d.cts +7 -32
- package/dist/cjs/utils.cjs +0 -4
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/esm/Matches.d.ts +3 -12
- package/dist/esm/Matches.js.map +1 -1
- package/dist/esm/await-signal.d.ts +1 -0
- package/dist/esm/await-signal.js +21 -0
- package/dist/esm/await-signal.js.map +1 -0
- package/dist/esm/index.d.ts +3 -2
- package/dist/esm/index.js +3 -2
- package/dist/esm/isServer/client.d.ts +1 -0
- package/dist/esm/isServer/client.js +2 -1
- package/dist/esm/isServer/client.js.map +1 -1
- package/dist/esm/isServer/development.d.ts +1 -0
- package/dist/esm/isServer/development.js +2 -1
- package/dist/esm/isServer/development.js.map +1 -1
- package/dist/esm/isServer/server.d.ts +1 -0
- package/dist/esm/isServer/server.js +2 -1
- package/dist/esm/isServer/server.js.map +1 -1
- package/dist/esm/load-client.d.ts +147 -0
- package/dist/esm/load-client.js +1462 -0
- package/dist/esm/load-client.js.map +1 -0
- package/dist/esm/load-server.d.ts +16 -0
- package/dist/esm/load-server.js +565 -0
- package/dist/esm/load-server.js.map +1 -0
- package/dist/esm/redirect.d.ts +0 -1
- package/dist/esm/redirect.js.map +1 -1
- package/dist/esm/route.js.map +1 -1
- package/dist/esm/router.d.ts +62 -52
- package/dist/esm/router.js +188 -446
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/scroll-restoration.js +27 -34
- package/dist/esm/scroll-restoration.js.map +1 -1
- package/dist/esm/ssr/client.js +1 -1
- package/dist/esm/ssr/createRequestHandler.d.ts +1 -0
- package/dist/esm/ssr/createRequestHandler.js +61 -11
- package/dist/esm/ssr/createRequestHandler.js.map +1 -1
- package/dist/esm/ssr/handlerCallback.d.ts +3 -0
- package/dist/esm/ssr/handlerCallback.js +47 -5
- package/dist/esm/ssr/handlerCallback.js.map +1 -1
- package/dist/esm/ssr/server.d.ts +2 -2
- package/dist/esm/ssr/server.js +3 -3
- package/dist/esm/ssr/ssr-client.d.ts +1 -10
- package/dist/esm/ssr/ssr-match-id.js +2 -2
- package/dist/esm/ssr/ssr-match-id.js.map +1 -1
- package/dist/esm/ssr/ssr-server.js +6 -6
- package/dist/esm/ssr/ssr-server.js.map +1 -1
- package/dist/esm/ssr/transformStreamWithRouter.d.ts +2 -0
- package/dist/esm/ssr/transformStreamWithRouter.js +27 -1
- package/dist/esm/ssr/transformStreamWithRouter.js.map +1 -1
- package/dist/esm/ssr/types.d.ts +0 -1
- package/dist/esm/stores.d.ts +7 -32
- package/dist/esm/stores.js +30 -104
- package/dist/esm/stores.js.map +1 -1
- package/dist/esm/utils.js +1 -4
- package/dist/esm/utils.js.map +1 -1
- package/package.json +3 -3
- package/src/Matches.ts +6 -21
- package/src/await-signal.ts +27 -0
- package/src/index.ts +1 -3
- package/src/isServer/client.ts +1 -0
- package/src/isServer/development.ts +1 -0
- package/src/isServer/server.ts +1 -0
- package/src/load-client.ts +2701 -0
- package/src/load-server.ts +849 -0
- package/src/redirect.ts +0 -1
- package/src/route.ts +2 -11
- package/src/router.ts +380 -774
- package/src/scroll-restoration.ts +40 -56
- package/src/ssr/createRequestHandler.ts +109 -16
- package/src/ssr/handlerCallback.ts +76 -10
- package/src/ssr/server.ts +4 -1
- package/src/ssr/ssr-client.ts +1 -310
- package/src/ssr/ssr-match-id.ts +11 -2
- package/src/ssr/ssr-server.ts +9 -8
- package/src/ssr/transformStreamWithRouter.ts +37 -1
- package/src/ssr/types.ts +0 -1
- package/src/stores.ts +46 -208
- package/dist/cjs/load-matches.cjs +0 -658
- package/dist/cjs/load-matches.cjs.map +0 -1
- package/dist/cjs/load-matches.d.cts +0 -18
- package/dist/cjs/ssr/ssr-client.cjs +0 -183
- package/dist/cjs/ssr/ssr-client.cjs.map +0 -1
- package/dist/esm/load-matches.d.ts +0 -18
- package/dist/esm/load-matches.js +0 -656
- package/dist/esm/load-matches.js.map +0 -1
- package/dist/esm/ssr/ssr-client.js +0 -183
- package/dist/esm/ssr/ssr-client.js.map +0 -1
- package/src/load-matches.ts +0 -1278
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { isServer } from '@tanstack/router-core/isServer'
|
|
2
|
-
import { locationHistoryActions } from './router'
|
|
3
2
|
import type { AnyRouter } from './router'
|
|
4
3
|
import type { ParsedLocation } from './location'
|
|
5
4
|
|
|
@@ -133,7 +132,6 @@ export function getElementScrollRestorationEntry(
|
|
|
133
132
|
|
|
134
133
|
let ignoreScroll = false
|
|
135
134
|
const windowScrollTarget = 'window'
|
|
136
|
-
type ScrollTarget = typeof windowScrollTarget | Element
|
|
137
135
|
|
|
138
136
|
function getElement(selector: string | (() => Element | null | undefined)) {
|
|
139
137
|
try {
|
|
@@ -148,8 +146,8 @@ function getScrollToTopElements(
|
|
|
148
146
|
scrollToTopSelectors: NonNullable<
|
|
149
147
|
AnyRouter['options']['scrollToTopSelectors']
|
|
150
148
|
>,
|
|
151
|
-
)
|
|
152
|
-
const elements
|
|
149
|
+
) {
|
|
150
|
+
const elements = new Set<Element>()
|
|
153
151
|
|
|
154
152
|
for (const selector of scrollToTopSelectors) {
|
|
155
153
|
if (selector === windowScrollTarget) {
|
|
@@ -158,7 +156,7 @@ function getScrollToTopElements(
|
|
|
158
156
|
|
|
159
157
|
const element = getElement(selector)
|
|
160
158
|
if (element) {
|
|
161
|
-
elements.
|
|
159
|
+
elements.add(element)
|
|
162
160
|
}
|
|
163
161
|
}
|
|
164
162
|
|
|
@@ -180,46 +178,21 @@ export function setupScrollRestoration(router: AnyRouter, force?: boolean) {
|
|
|
180
178
|
|
|
181
179
|
const getKey =
|
|
182
180
|
router.options.getScrollRestorationKey || defaultGetScrollRestorationKey
|
|
183
|
-
const
|
|
184
|
-
const setTrackedScrollEntry = (
|
|
185
|
-
target: ScrollTarget,
|
|
186
|
-
scrollX: number,
|
|
187
|
-
scrollY: number,
|
|
188
|
-
) => {
|
|
189
|
-
const entry =
|
|
190
|
-
trackedScrollEntries.get(target) || ({} as ScrollRestorationEntry)
|
|
191
|
-
entry.scrollX = scrollX
|
|
192
|
-
entry.scrollY = scrollY
|
|
193
|
-
trackedScrollEntries.set(target, entry)
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const onScroll = (event: Event) => {
|
|
197
|
-
if (ignoreScroll || !scroll.restoring) {
|
|
198
|
-
return
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
if (event.target === document) {
|
|
202
|
-
setTrackedScrollEntry(windowScrollTarget, scrollX, scrollY)
|
|
203
|
-
} else {
|
|
204
|
-
const target = event.target as Element
|
|
205
|
-
setTrackedScrollEntry(target, target.scrollLeft, target.scrollTop)
|
|
206
|
-
}
|
|
207
|
-
}
|
|
181
|
+
const trackedScrollTargets = new Set<Document | Element>()
|
|
208
182
|
|
|
209
183
|
// Snapshot the current page's tracked scroll targets before navigation or unload.
|
|
210
184
|
const snapshotCurrentScrollTargets = (restoreKey: string) => {
|
|
211
|
-
if (!scroll.restoring) {
|
|
212
|
-
return
|
|
213
|
-
}
|
|
214
|
-
|
|
215
185
|
const keyEntry = (scrollRestorationCache[restoreKey] ||=
|
|
216
186
|
{} as ScrollRestorationByElement)
|
|
217
187
|
|
|
218
|
-
for (const
|
|
219
|
-
if (target ===
|
|
220
|
-
keyEntry[windowScrollTarget] =
|
|
221
|
-
} else if (target.isConnected) {
|
|
222
|
-
keyEntry[getScrollRestorationSelector(target)] =
|
|
188
|
+
for (const target of trackedScrollTargets) {
|
|
189
|
+
if (target === document) {
|
|
190
|
+
keyEntry[windowScrollTarget] = { scrollX, scrollY }
|
|
191
|
+
} else if ((target as Element).isConnected) {
|
|
192
|
+
keyEntry[getScrollRestorationSelector(target as Element)] = {
|
|
193
|
+
scrollX: (target as Element).scrollLeft,
|
|
194
|
+
scrollY: (target as Element).scrollTop,
|
|
195
|
+
}
|
|
223
196
|
}
|
|
224
197
|
}
|
|
225
198
|
}
|
|
@@ -230,12 +203,21 @@ export function setupScrollRestoration(router: AnyRouter, force?: boolean) {
|
|
|
230
203
|
|
|
231
204
|
history.scrollRestoration = 'manual'
|
|
232
205
|
|
|
233
|
-
document.addEventListener(
|
|
206
|
+
document.addEventListener(
|
|
207
|
+
'scroll',
|
|
208
|
+
(event) => {
|
|
209
|
+
if (ignoreScroll) {
|
|
210
|
+
return
|
|
211
|
+
}
|
|
212
|
+
trackedScrollTargets.add(event.target as Document | Element)
|
|
213
|
+
},
|
|
214
|
+
true,
|
|
215
|
+
)
|
|
234
216
|
router.subscribe('onBeforeLoad', (event) => {
|
|
235
217
|
if (event.fromLocation) {
|
|
236
218
|
snapshotCurrentScrollTargets(getKey(event.fromLocation))
|
|
237
219
|
}
|
|
238
|
-
|
|
220
|
+
trackedScrollTargets.clear()
|
|
239
221
|
})
|
|
240
222
|
addEventListener('pagehide', () => {
|
|
241
223
|
snapshotCurrentScrollTargets(
|
|
@@ -258,12 +240,11 @@ export function setupScrollRestoration(router: AnyRouter, force?: boolean) {
|
|
|
258
240
|
const behavior = router.options.scrollRestorationBehavior
|
|
259
241
|
const scrollToTopSelectors = router.options.scrollToTopSelectors
|
|
260
242
|
const shouldResetScroll = scroll.next
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
}
|
|
243
|
+
const hashNavigation = scroll.hash
|
|
244
|
+
let scrollToTopElements: Set<Element> | undefined
|
|
245
|
+
trackedScrollTargets.clear()
|
|
246
|
+
scroll.next = true
|
|
247
|
+
scroll.hash = false
|
|
267
248
|
|
|
268
249
|
if (
|
|
269
250
|
typeof router.options.scrollRestoration === 'function' &&
|
|
@@ -295,7 +276,7 @@ export function setupScrollRestoration(router: AnyRouter, force?: boolean) {
|
|
|
295
276
|
if (shouldResetScroll && scrollToTopSelectors) {
|
|
296
277
|
scrollToTopElements ??=
|
|
297
278
|
getScrollToTopElements(scrollToTopSelectors)
|
|
298
|
-
if (scrollToTopElements.
|
|
279
|
+
if (scrollToTopElements.has(element)) {
|
|
299
280
|
continue
|
|
300
281
|
}
|
|
301
282
|
}
|
|
@@ -321,11 +302,12 @@ export function setupScrollRestoration(router: AnyRouter, force?: boolean) {
|
|
|
321
302
|
let windowRestored = false
|
|
322
303
|
|
|
323
304
|
if (shouldResetScroll) {
|
|
324
|
-
|
|
305
|
+
if (!hash && scrollToTopSelectors) {
|
|
306
|
+
scrollToTopElements ??= getScrollToTopElements(scrollToTopSelectors)
|
|
307
|
+
}
|
|
308
|
+
|
|
325
309
|
const skipWindowRestore =
|
|
326
|
-
hash &&
|
|
327
|
-
hashScrollIntoViewOptions &&
|
|
328
|
-
(action === 'PUSH' || action === 'REPLACE')
|
|
310
|
+
hash && hashScrollIntoViewOptions && hashNavigation
|
|
329
311
|
|
|
330
312
|
const elementEntries = scroll.restoring
|
|
331
313
|
? scrollRestorationCache[cacheKey]
|
|
@@ -351,21 +333,23 @@ export function setupScrollRestoration(router: AnyRouter, force?: boolean) {
|
|
|
351
333
|
if (element) {
|
|
352
334
|
element.scrollLeft = scrollX
|
|
353
335
|
element.scrollTop = scrollY
|
|
336
|
+
scrollToTopElements?.delete(element)
|
|
354
337
|
}
|
|
355
338
|
}
|
|
356
339
|
}
|
|
357
340
|
}
|
|
358
341
|
|
|
359
|
-
if (!
|
|
342
|
+
if (!hash) {
|
|
360
343
|
const scrollOptions = {
|
|
361
344
|
top: 0,
|
|
362
345
|
left: 0,
|
|
363
346
|
behavior,
|
|
364
347
|
}
|
|
365
348
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
349
|
+
if (!windowRestored) {
|
|
350
|
+
scrollTo(scrollOptions)
|
|
351
|
+
}
|
|
352
|
+
if (scrollToTopElements) {
|
|
369
353
|
for (const element of scrollToTopElements) {
|
|
370
354
|
element.scrollTo(scrollOptions)
|
|
371
355
|
}
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { createMemoryHistory } from '@tanstack/history'
|
|
2
|
+
import { _getRenderedMatches } from '../load-client'
|
|
2
3
|
import { mergeHeaders } from './headers'
|
|
3
4
|
import {
|
|
4
5
|
attachRouterServerSsrUtils,
|
|
5
6
|
getNormalizedURL,
|
|
6
7
|
getOrigin,
|
|
7
8
|
} from './ssr-server'
|
|
8
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
bindSsrResponseToRequest,
|
|
11
|
+
disposeSsrResponseDetached,
|
|
12
|
+
} from './handlerCallback'
|
|
9
13
|
import type { HandlerCallback } from './handlerCallback'
|
|
10
14
|
import type { AnyHeaders } from './headers'
|
|
11
15
|
import type { AnyRouter } from '../router'
|
|
@@ -15,6 +19,79 @@ export type RequestHandler<TRouter extends AnyRouter> = (
|
|
|
15
19
|
cb: HandlerCallback<TRouter>,
|
|
16
20
|
) => Promise<Response>
|
|
17
21
|
|
|
22
|
+
type RequestWaiter = ((reason: unknown) => void) | undefined
|
|
23
|
+
|
|
24
|
+
const requestWaiters = new WeakMap<AbortSignal, Array<RequestWaiter>>()
|
|
25
|
+
|
|
26
|
+
function removeRequestWaiter(
|
|
27
|
+
waiters: Array<RequestWaiter>,
|
|
28
|
+
index: number,
|
|
29
|
+
reject: (reason: unknown) => void,
|
|
30
|
+
) {
|
|
31
|
+
if (waiters[index] !== reject) {
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
if (index !== waiters.length - 1) {
|
|
35
|
+
waiters[index] = undefined
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
waiters.pop()
|
|
40
|
+
while (waiters.length && waiters[waiters.length - 1] === undefined) {
|
|
41
|
+
waiters.pop()
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function waitForRequest<T>(
|
|
46
|
+
value: T | PromiseLike<T>,
|
|
47
|
+
signal: AbortSignal,
|
|
48
|
+
onLate?: (value: T) => void,
|
|
49
|
+
): Promise<T> {
|
|
50
|
+
const promise = Promise.resolve(value)
|
|
51
|
+
if (signal.aborted) {
|
|
52
|
+
void promise.then(onLate, () => {})
|
|
53
|
+
return Promise.reject(signal.reason)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return new Promise<T>((resolve, reject) => {
|
|
57
|
+
let waiters = requestWaiters.get(signal)
|
|
58
|
+
let index: number
|
|
59
|
+
if (waiters) {
|
|
60
|
+
index = waiters.push(reject) - 1
|
|
61
|
+
} else {
|
|
62
|
+
const newWaiters: Array<RequestWaiter> = [reject]
|
|
63
|
+
waiters = newWaiters
|
|
64
|
+
index = 0
|
|
65
|
+
requestWaiters.set(signal, newWaiters)
|
|
66
|
+
signal.addEventListener(
|
|
67
|
+
'abort',
|
|
68
|
+
() => {
|
|
69
|
+
requestWaiters.delete(signal)
|
|
70
|
+
for (const rejectWaiter of newWaiters) {
|
|
71
|
+
rejectWaiter?.(signal.reason)
|
|
72
|
+
}
|
|
73
|
+
newWaiters.length = 0
|
|
74
|
+
},
|
|
75
|
+
{ once: true },
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
void promise.then(
|
|
79
|
+
(result) => {
|
|
80
|
+
removeRequestWaiter(waiters, index, reject)
|
|
81
|
+
if (signal.aborted) {
|
|
82
|
+
onLate?.(result)
|
|
83
|
+
} else {
|
|
84
|
+
resolve(result)
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
(error) => {
|
|
88
|
+
removeRequestWaiter(waiters, index, reject)
|
|
89
|
+
reject(error)
|
|
90
|
+
},
|
|
91
|
+
)
|
|
92
|
+
})
|
|
93
|
+
}
|
|
94
|
+
|
|
18
95
|
export function createRequestHandler<TRouter extends AnyRouter>({
|
|
19
96
|
createRouter,
|
|
20
97
|
request,
|
|
@@ -25,13 +102,14 @@ export function createRequestHandler<TRouter extends AnyRouter>({
|
|
|
25
102
|
getRouterManifest?: () => ServerManifest | Promise<ServerManifest>
|
|
26
103
|
}): RequestHandler<TRouter> {
|
|
27
104
|
return async (cb) => {
|
|
105
|
+
request.signal.throwIfAborted()
|
|
28
106
|
const router = createRouter()
|
|
29
107
|
let responseOwnsCleanup = false
|
|
30
108
|
|
|
31
109
|
try {
|
|
32
110
|
attachRouterServerSsrUtils({
|
|
33
111
|
router,
|
|
34
|
-
manifest: await getRouterManifest?.(),
|
|
112
|
+
manifest: await waitForRequest(getRouterManifest?.(), request.signal),
|
|
35
113
|
})
|
|
36
114
|
|
|
37
115
|
// normalizing and sanitizing the pathname here for server, so we always deal with the same format during SSR.
|
|
@@ -50,20 +128,41 @@ export function createRequestHandler<TRouter extends AnyRouter>({
|
|
|
50
128
|
origin: router.options.origin ?? origin,
|
|
51
129
|
})
|
|
52
130
|
|
|
53
|
-
await router.load(
|
|
131
|
+
await router.load({
|
|
132
|
+
_signal: request.signal,
|
|
133
|
+
})
|
|
134
|
+
request.signal.throwIfAborted()
|
|
135
|
+
|
|
136
|
+
const result = router._serverResult
|
|
137
|
+
if (result?.type === 'redirect') {
|
|
138
|
+
return result.redirect
|
|
139
|
+
}
|
|
54
140
|
|
|
55
|
-
await router.serverSsr?.dehydrate()
|
|
141
|
+
await waitForRequest(router.serverSsr?.dehydrate(), request.signal)
|
|
142
|
+
request.signal.throwIfAborted()
|
|
56
143
|
|
|
57
144
|
const responseHeaders = getRequestHeaders({
|
|
58
145
|
router,
|
|
59
146
|
})
|
|
60
147
|
|
|
61
|
-
|
|
62
|
-
|
|
148
|
+
request.signal.throwIfAborted()
|
|
149
|
+
const response = await waitForRequest(
|
|
150
|
+
cb({
|
|
151
|
+
request,
|
|
152
|
+
router,
|
|
153
|
+
responseHeaders,
|
|
154
|
+
}),
|
|
155
|
+
request.signal,
|
|
156
|
+
(late) => {
|
|
157
|
+
disposeSsrResponseDetached(late, request.signal.reason)
|
|
158
|
+
},
|
|
159
|
+
)
|
|
160
|
+
const ssrResponse = bindSsrResponseToRequest(
|
|
63
161
|
router,
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
162
|
+
response,
|
|
163
|
+
request.signal,
|
|
164
|
+
)
|
|
165
|
+
request.signal.throwIfAborted()
|
|
67
166
|
responseOwnsCleanup = ssrResponse.serverSsrCleanup === 'stream'
|
|
68
167
|
return ssrResponse.response
|
|
69
168
|
} finally {
|
|
@@ -79,16 +178,10 @@ export function createRequestHandler<TRouter extends AnyRouter>({
|
|
|
79
178
|
|
|
80
179
|
function getRequestHeaders(opts: { router: AnyRouter }): Headers {
|
|
81
180
|
const matchHeaders: Array<AnyHeaders> = []
|
|
82
|
-
for (const match of opts.router.stores.matches.get()) {
|
|
181
|
+
for (const match of _getRenderedMatches(opts.router.stores.matches.get())) {
|
|
83
182
|
matchHeaders.push(match.headers)
|
|
84
183
|
}
|
|
85
184
|
|
|
86
|
-
// Handle Redirects
|
|
87
|
-
const redirect = opts.router.stores.redirect.get()
|
|
88
|
-
if (redirect) {
|
|
89
|
-
matchHeaders.push(redirect.headers)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
185
|
return mergeHeaders(
|
|
93
186
|
{
|
|
94
187
|
'Content-Type': 'text/html; charset=UTF-8',
|
|
@@ -30,6 +30,40 @@ export function normalizeSsrResponse(
|
|
|
30
30
|
: { response: result, serverSsrCleanup: 'none' }
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
export function disposeSsrResponse(
|
|
34
|
+
response: SsrResponse,
|
|
35
|
+
reason?: unknown,
|
|
36
|
+
): Promise<void> {
|
|
37
|
+
if (response.serverSsrCleanup !== 'stream') {
|
|
38
|
+
return Promise.resolve()
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
return Promise.resolve(response.dispose(reason))
|
|
42
|
+
} catch (error) {
|
|
43
|
+
return Promise.reject(error)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function disposeSsrResponseDetached(
|
|
48
|
+
result: HandlerCallbackResult,
|
|
49
|
+
reason?: unknown,
|
|
50
|
+
onError: (error: unknown) => void = console.error,
|
|
51
|
+
): void {
|
|
52
|
+
const ssrResponse = normalizeSsrResponse(result)
|
|
53
|
+
if (ssrResponse.serverSsrCleanup === 'stream') {
|
|
54
|
+
void disposeSsrResponse(ssrResponse, reason).catch(onError)
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (ssrResponse.response.body) {
|
|
59
|
+
try {
|
|
60
|
+
void ssrResponse.response.body.cancel(reason).catch(onError)
|
|
61
|
+
} catch (error) {
|
|
62
|
+
onError(error)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
33
67
|
export function createSsrStreamResponse<TRouter extends AnyRouter>(
|
|
34
68
|
router: TRouter,
|
|
35
69
|
response: Response,
|
|
@@ -43,29 +77,63 @@ export function createSsrStreamResponse<TRouter extends AnyRouter>(
|
|
|
43
77
|
response,
|
|
44
78
|
serverSsrCleanup: 'stream',
|
|
45
79
|
async dispose(reason?: unknown) {
|
|
46
|
-
if (disposed)
|
|
80
|
+
if (disposed) {
|
|
81
|
+
return
|
|
82
|
+
}
|
|
47
83
|
disposed = true
|
|
48
84
|
|
|
85
|
+
// Sever router ownership before asking user/renderer stream machinery to
|
|
86
|
+
// cancel. A custom stream is allowed to ignore cancellation forever.
|
|
87
|
+
router.serverSsr?.cleanup()
|
|
88
|
+
|
|
49
89
|
try {
|
|
50
90
|
await response.body!.cancel(reason)
|
|
51
91
|
} catch {
|
|
52
|
-
//
|
|
92
|
+
// Cleanup above already released router SSR state.
|
|
53
93
|
}
|
|
54
|
-
|
|
55
|
-
router.serverSsr?.cleanup()
|
|
56
94
|
},
|
|
57
95
|
}
|
|
58
96
|
}
|
|
59
97
|
|
|
98
|
+
export function bindSsrResponseToRequest(
|
|
99
|
+
router: AnyRouter | undefined,
|
|
100
|
+
result: HandlerCallbackResult,
|
|
101
|
+
signal: AbortSignal,
|
|
102
|
+
): SsrResponse {
|
|
103
|
+
const ssrResponse = normalizeSsrResponse(result)
|
|
104
|
+
if (ssrResponse.serverSsrCleanup !== 'stream') {
|
|
105
|
+
if (signal.aborted) {
|
|
106
|
+
disposeSsrResponseDetached(result, signal.reason)
|
|
107
|
+
}
|
|
108
|
+
return ssrResponse
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const failed = (error: unknown) => {
|
|
112
|
+
router?.serverSsr?.cleanup()
|
|
113
|
+
console.error(error)
|
|
114
|
+
}
|
|
115
|
+
const abort = () => {
|
|
116
|
+
disposeSsrResponseDetached(ssrResponse, signal.reason, failed)
|
|
117
|
+
}
|
|
118
|
+
if (signal.aborted) {
|
|
119
|
+
abort()
|
|
120
|
+
return ssrResponse
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
signal.addEventListener('abort', abort, { once: true })
|
|
124
|
+
router?.serverSsr?.onCleanup(() => {
|
|
125
|
+
signal.removeEventListener('abort', abort)
|
|
126
|
+
})
|
|
127
|
+
return ssrResponse
|
|
128
|
+
}
|
|
129
|
+
|
|
60
130
|
export async function replaceSsrResponse(
|
|
61
131
|
result: HandlerCallbackResult,
|
|
62
132
|
response: Response,
|
|
63
133
|
reason?: unknown,
|
|
64
134
|
): Promise<SsrResponse> {
|
|
65
135
|
const ssrResponse = normalizeSsrResponse(result)
|
|
66
|
-
|
|
67
|
-
await ssrResponse.dispose(reason)
|
|
68
|
-
}
|
|
136
|
+
await disposeSsrResponse(ssrResponse, reason)
|
|
69
137
|
return { response, serverSsrCleanup: 'none' }
|
|
70
138
|
}
|
|
71
139
|
|
|
@@ -74,9 +142,7 @@ export async function stripSsrResponseBody(
|
|
|
74
142
|
reason?: unknown,
|
|
75
143
|
): Promise<SsrResponse> {
|
|
76
144
|
const ssrResponse = normalizeSsrResponse(result)
|
|
77
|
-
|
|
78
|
-
await ssrResponse.dispose(reason)
|
|
79
|
-
}
|
|
145
|
+
await disposeSsrResponse(ssrResponse, reason)
|
|
80
146
|
return {
|
|
81
147
|
response: new Response(null, ssrResponse.response),
|
|
82
148
|
serverSsrCleanup: 'none',
|
package/src/ssr/server.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
export { createRequestHandler } from './createRequestHandler'
|
|
1
|
+
export { createRequestHandler, waitForRequest } from './createRequestHandler'
|
|
2
2
|
export type { RequestHandler } from './createRequestHandler'
|
|
3
3
|
export {
|
|
4
|
+
bindSsrResponseToRequest,
|
|
4
5
|
createSsrStreamResponse,
|
|
5
6
|
defineHandlerCallback,
|
|
7
|
+
disposeSsrResponse,
|
|
8
|
+
disposeSsrResponseDetached,
|
|
6
9
|
isSsrResponse,
|
|
7
10
|
normalizeSsrResponse,
|
|
8
11
|
replaceSsrResponse,
|