@tanstack/router-core 1.171.5 → 1.171.7
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/config.cjs.map +1 -1
- package/dist/cjs/defer.cjs.map +1 -1
- package/dist/cjs/index.cjs +5 -1
- package/dist/cjs/index.d.cts +2 -2
- package/dist/cjs/invariant.cjs.map +1 -1
- package/dist/cjs/load-matches.cjs.map +1 -1
- package/dist/cjs/lru-cache.cjs.map +1 -1
- package/dist/cjs/manifest.cjs +43 -17
- package/dist/cjs/manifest.cjs.map +1 -1
- package/dist/cjs/manifest.d.cts +76 -24
- package/dist/cjs/new-process-route-tree.cjs.map +1 -1
- package/dist/cjs/not-found.cjs.map +1 -1
- package/dist/cjs/path.cjs.map +1 -1
- package/dist/cjs/qss.cjs.map +1 -1
- package/dist/cjs/redirect.cjs.map +1 -1
- package/dist/cjs/rewrite.cjs.map +1 -1
- package/dist/cjs/route.cjs.map +1 -1
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/router.d.cts +31 -16
- package/dist/cjs/scroll-restoration-script/client.cjs.map +1 -1
- package/dist/cjs/scroll-restoration-script/server.cjs.map +1 -1
- package/dist/cjs/scroll-restoration.cjs.map +1 -1
- package/dist/cjs/searchMiddleware.cjs.map +1 -1
- package/dist/cjs/searchParams.cjs.map +1 -1
- package/dist/cjs/ssr/createRequestHandler.cjs +10 -8
- package/dist/cjs/ssr/createRequestHandler.cjs.map +1 -1
- package/dist/cjs/ssr/createRequestHandler.d.cts +2 -2
- package/dist/cjs/ssr/handlerCallback.cjs +46 -0
- package/dist/cjs/ssr/handlerCallback.cjs.map +1 -1
- package/dist/cjs/ssr/handlerCallback.d.cts +15 -1
- package/dist/cjs/ssr/headers.cjs.map +1 -1
- package/dist/cjs/ssr/json.cjs.map +1 -1
- package/dist/cjs/ssr/serializer/RawStream.cjs.map +1 -1
- package/dist/cjs/ssr/serializer/ShallowErrorPlugin.cjs.map +1 -1
- package/dist/cjs/ssr/serializer/seroval-plugins.cjs.map +1 -1
- package/dist/cjs/ssr/serializer/transformer.cjs.map +1 -1
- package/dist/cjs/ssr/server.cjs +6 -1
- package/dist/cjs/ssr/server.d.cts +3 -2
- package/dist/cjs/ssr/ssr-client.cjs.map +1 -1
- package/dist/cjs/ssr/ssr-match-id.cjs.map +1 -1
- package/dist/cjs/ssr/ssr-server.cjs +263 -132
- package/dist/cjs/ssr/ssr-server.cjs.map +1 -1
- package/dist/cjs/ssr/ssr-server.d.cts +4 -19
- package/dist/cjs/ssr/transformStreamWithRouter.cjs +455 -203
- package/dist/cjs/ssr/transformStreamWithRouter.cjs.map +1 -1
- package/dist/cjs/ssr/transformStreamWithRouter.d.cts +14 -5
- package/dist/cjs/stores.cjs.map +1 -1
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/esm/Matches.js.map +1 -1
- package/dist/esm/config.js.map +1 -1
- package/dist/esm/defer.js.map +1 -1
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.js +2 -2
- package/dist/esm/invariant.js.map +1 -1
- package/dist/esm/load-matches.js.map +1 -1
- package/dist/esm/lru-cache.js.map +1 -1
- package/dist/esm/manifest.d.ts +76 -24
- package/dist/esm/manifest.js +39 -17
- package/dist/esm/manifest.js.map +1 -1
- package/dist/esm/new-process-route-tree.js.map +1 -1
- package/dist/esm/not-found.js.map +1 -1
- package/dist/esm/path.js.map +1 -1
- package/dist/esm/qss.js.map +1 -1
- package/dist/esm/redirect.js.map +1 -1
- package/dist/esm/rewrite.js.map +1 -1
- package/dist/esm/route.js.map +1 -1
- package/dist/esm/router.d.ts +31 -16
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/scroll-restoration-script/client.js.map +1 -1
- package/dist/esm/scroll-restoration-script/server.js.map +1 -1
- package/dist/esm/scroll-restoration.js.map +1 -1
- package/dist/esm/searchMiddleware.js.map +1 -1
- package/dist/esm/searchParams.js.map +1 -1
- package/dist/esm/ssr/createRequestHandler.d.ts +2 -2
- package/dist/esm/ssr/createRequestHandler.js +10 -8
- package/dist/esm/ssr/createRequestHandler.js.map +1 -1
- package/dist/esm/ssr/handlerCallback.d.ts +15 -1
- package/dist/esm/ssr/handlerCallback.js +42 -1
- package/dist/esm/ssr/handlerCallback.js.map +1 -1
- package/dist/esm/ssr/headers.js.map +1 -1
- package/dist/esm/ssr/json.js.map +1 -1
- package/dist/esm/ssr/serializer/RawStream.js.map +1 -1
- package/dist/esm/ssr/serializer/ShallowErrorPlugin.js.map +1 -1
- package/dist/esm/ssr/serializer/seroval-plugins.js.map +1 -1
- package/dist/esm/ssr/serializer/transformer.js.map +1 -1
- package/dist/esm/ssr/server.d.ts +3 -2
- package/dist/esm/ssr/server.js +2 -2
- package/dist/esm/ssr/ssr-client.js.map +1 -1
- package/dist/esm/ssr/ssr-match-id.js.map +1 -1
- package/dist/esm/ssr/ssr-server.d.ts +4 -19
- package/dist/esm/ssr/ssr-server.js +264 -133
- package/dist/esm/ssr/ssr-server.js.map +1 -1
- package/dist/esm/ssr/transformStreamWithRouter.d.ts +14 -5
- package/dist/esm/ssr/transformStreamWithRouter.js +455 -203
- package/dist/esm/ssr/transformStreamWithRouter.js.map +1 -1
- package/dist/esm/stores.js.map +1 -1
- package/dist/esm/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +21 -1
- package/src/manifest.ts +151 -59
- package/src/router.ts +37 -19
- package/src/ssr/createRequestHandler.ts +14 -13
- package/src/ssr/handlerCallback.ts +84 -1
- package/src/ssr/server.ts +14 -2
- package/src/ssr/ssr-server.ts +418 -222
- package/src/ssr/transformStreamWithRouter.ts +662 -281
package/src/manifest.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
export type AssetCrossOrigin = 'anonymous' | 'use-credentials'
|
|
2
|
+
export type ScriptFormat = 'module' | 'iife'
|
|
3
|
+
|
|
4
|
+
export const DEV_STYLES_ATTR = 'data-tanstack-router-dev-styles'
|
|
2
5
|
|
|
3
6
|
export type AssetCrossOriginConfig =
|
|
4
7
|
| AssetCrossOrigin
|
|
5
|
-
| Partial<Record<'
|
|
8
|
+
| Partial<Record<'script' | 'stylesheet', AssetCrossOrigin>>
|
|
6
9
|
|
|
7
10
|
export type ManifestAssetLink =
|
|
8
11
|
| string
|
|
@@ -13,7 +16,7 @@ export type ManifestAssetLink =
|
|
|
13
16
|
|
|
14
17
|
export function getAssetCrossOrigin(
|
|
15
18
|
assetCrossOrigin: AssetCrossOriginConfig | undefined,
|
|
16
|
-
kind: '
|
|
19
|
+
kind: 'script' | 'stylesheet',
|
|
17
20
|
): AssetCrossOrigin | undefined {
|
|
18
21
|
if (!assetCrossOrigin) {
|
|
19
22
|
return undefined
|
|
@@ -26,6 +29,35 @@ export function getAssetCrossOrigin(
|
|
|
26
29
|
return assetCrossOrigin[kind]
|
|
27
30
|
}
|
|
28
31
|
|
|
32
|
+
export function getManifestScriptFormat(
|
|
33
|
+
manifest: { scriptFormat?: ScriptFormat } | undefined,
|
|
34
|
+
): ScriptFormat {
|
|
35
|
+
return manifest?.scriptFormat ?? 'module'
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function getScriptPreloadAttrs(
|
|
39
|
+
manifest: { scriptFormat?: ScriptFormat } | undefined,
|
|
40
|
+
link: ManifestAssetLink,
|
|
41
|
+
assetCrossOrigin?: AssetCrossOriginConfig,
|
|
42
|
+
): {
|
|
43
|
+
rel: 'modulepreload' | 'preload'
|
|
44
|
+
as?: 'script'
|
|
45
|
+
href: string
|
|
46
|
+
crossOrigin?: AssetCrossOrigin
|
|
47
|
+
} {
|
|
48
|
+
const preloadLink = resolveManifestAssetLink(link)
|
|
49
|
+
const crossOrigin =
|
|
50
|
+
getAssetCrossOrigin(assetCrossOrigin, 'script') ?? preloadLink.crossOrigin
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
...(getManifestScriptFormat(manifest) === 'iife'
|
|
54
|
+
? { rel: 'preload', as: 'script' }
|
|
55
|
+
: { rel: 'modulepreload' }),
|
|
56
|
+
href: preloadLink.href,
|
|
57
|
+
...(crossOrigin ? { crossOrigin } : {}),
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
29
61
|
export function resolveManifestAssetLink(link: ManifestAssetLink) {
|
|
30
62
|
if (typeof link === 'string') {
|
|
31
63
|
return { href: link, crossOrigin: undefined }
|
|
@@ -35,87 +67,147 @@ export function resolveManifestAssetLink(link: ManifestAssetLink) {
|
|
|
35
67
|
}
|
|
36
68
|
|
|
37
69
|
export type Manifest = {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
>
|
|
70
|
+
scriptFormat?: ScriptFormat
|
|
71
|
+
inlineStyle?: ManifestInlineCss
|
|
72
|
+
routes: Record<string, ManifestRoute>
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export type ServerManifest = {
|
|
76
|
+
scriptFormat?: ScriptFormat
|
|
77
|
+
inlineCss?: ServerManifestInlineCss
|
|
78
|
+
routes: Record<string, ServerManifestRoute>
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export type ServerManifestInlineCss = {
|
|
82
|
+
styles: Record<string, string>
|
|
83
|
+
templates?: Record<string, InlineCssTemplate>
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export type InlineCssTemplate = {
|
|
87
|
+
strings: Array<string>
|
|
88
|
+
urls: Array<string>
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export type ManifestRoute = {
|
|
92
|
+
filePath?: string
|
|
93
|
+
preloads?: Array<ManifestAssetLink>
|
|
94
|
+
scripts?: Array<ManifestScript>
|
|
95
|
+
css?: Array<ManifestCssLink>
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export type ServerManifestRoute = ManifestRoute
|
|
99
|
+
|
|
100
|
+
export type ManifestRouteAssets = Pick<
|
|
101
|
+
ManifestRoute,
|
|
102
|
+
'preloads' | 'scripts' | 'css'
|
|
103
|
+
>
|
|
104
|
+
|
|
105
|
+
export type RouterManagedTitleTag = {
|
|
106
|
+
tag: 'title'
|
|
107
|
+
attrs?: Record<string, any>
|
|
108
|
+
children: string
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export type RouterManagedMetaTag = {
|
|
112
|
+
tag: 'meta'
|
|
113
|
+
attrs?: Record<string, any>
|
|
114
|
+
children?: never
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export type RouterManagedLinkTag = {
|
|
118
|
+
tag: 'link'
|
|
119
|
+
attrs?: Record<string, any>
|
|
120
|
+
children?: never
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export type RouterManagedScriptTag = {
|
|
124
|
+
tag: 'script'
|
|
125
|
+
attrs?: Record<string, any>
|
|
126
|
+
children?: string
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export type ManifestScript = Omit<RouterManagedScriptTag, 'tag'>
|
|
130
|
+
|
|
131
|
+
export type RouterManagedStyleTag = {
|
|
132
|
+
tag: 'style'
|
|
133
|
+
attrs?: Record<string, any>
|
|
134
|
+
children?: string
|
|
135
|
+
inlineCss?: true
|
|
56
136
|
}
|
|
57
137
|
|
|
58
138
|
export type RouterManagedTag =
|
|
59
|
-
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
139
|
+
| RouterManagedTitleTag
|
|
140
|
+
| RouterManagedMetaTag
|
|
141
|
+
| RouterManagedLinkTag
|
|
142
|
+
| RouterManagedScriptTag
|
|
143
|
+
| RouterManagedStyleTag
|
|
144
|
+
|
|
145
|
+
export function appendUniqueUserTags(
|
|
146
|
+
target: Array<RouterManagedTag>,
|
|
147
|
+
tags: Array<RouterManagedTag>,
|
|
148
|
+
) {
|
|
149
|
+
if (tags.length === 0) {
|
|
150
|
+
return
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (tags.length === 1) {
|
|
154
|
+
target.push(tags[0]!)
|
|
155
|
+
return
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const seen = new Set<string>()
|
|
159
|
+
for (const tag of tags) {
|
|
160
|
+
const key = JSON.stringify(tag)
|
|
161
|
+
if (seen.has(key)) {
|
|
162
|
+
continue
|
|
73
163
|
}
|
|
164
|
+
seen.add(key)
|
|
165
|
+
target.push(tag)
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export type ManifestCssLink =
|
|
170
|
+
| string
|
|
74
171
|
| {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
inlineCss?: true
|
|
172
|
+
href: string
|
|
173
|
+
crossOrigin?: AssetCrossOrigin
|
|
174
|
+
[DEV_STYLES_ATTR]?: true
|
|
79
175
|
}
|
|
80
176
|
|
|
81
|
-
export
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const href = asset.attrs?.href
|
|
86
|
-
if (typeof href !== 'string') return undefined
|
|
177
|
+
export type ManifestInlineCss = {
|
|
178
|
+
attrs?: Record<string, any>
|
|
179
|
+
children?: string
|
|
180
|
+
}
|
|
87
181
|
|
|
88
|
-
|
|
89
|
-
|
|
182
|
+
export type RouterManagedInlineCssTag = RouterManagedStyleTag & {
|
|
183
|
+
inlineCss: true
|
|
184
|
+
}
|
|
90
185
|
|
|
91
|
-
|
|
186
|
+
export function getStylesheetHref(asset: ManifestCssLink) {
|
|
187
|
+
return resolveManifestCssLink(asset).href
|
|
92
188
|
}
|
|
93
189
|
|
|
94
|
-
export function
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
return
|
|
190
|
+
export function resolveManifestCssLink(link: ManifestCssLink) {
|
|
191
|
+
if (typeof link === 'string') {
|
|
192
|
+
return { href: link, crossOrigin: undefined }
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return link
|
|
100
196
|
}
|
|
101
197
|
|
|
102
|
-
export function createInlineCssStyleAsset(css: string):
|
|
198
|
+
export function createInlineCssStyleAsset(css: string): ManifestInlineCss {
|
|
103
199
|
return {
|
|
104
|
-
tag: 'style',
|
|
105
200
|
attrs: {
|
|
106
201
|
suppressHydrationWarning: true,
|
|
107
202
|
},
|
|
108
|
-
inlineCss: true,
|
|
109
203
|
children: css,
|
|
110
204
|
}
|
|
111
205
|
}
|
|
112
206
|
|
|
113
|
-
export function createInlineCssPlaceholderAsset():
|
|
207
|
+
export function createInlineCssPlaceholderAsset(): ManifestInlineCss {
|
|
114
208
|
return {
|
|
115
|
-
tag: 'style',
|
|
116
209
|
attrs: {
|
|
117
210
|
suppressHydrationWarning: true,
|
|
118
211
|
},
|
|
119
|
-
inlineCss: true,
|
|
120
212
|
}
|
|
121
213
|
}
|
package/src/router.ts
CHANGED
|
@@ -98,7 +98,11 @@ import type {
|
|
|
98
98
|
CommitLocationOptions,
|
|
99
99
|
NavigateFn,
|
|
100
100
|
} from './RouterProvider'
|
|
101
|
-
import type {
|
|
101
|
+
import type {
|
|
102
|
+
Manifest,
|
|
103
|
+
ManifestRouteAssets,
|
|
104
|
+
RouterManagedTag,
|
|
105
|
+
} from './manifest'
|
|
102
106
|
import type { AnySchema, AnyValidator } from './validators'
|
|
103
107
|
import type { NavigateOptions, ResolveRelativePath, ToOptions } from './link'
|
|
104
108
|
import type { NotFoundError } from './not-found'
|
|
@@ -341,9 +345,8 @@ export interface RouterOptions<
|
|
|
341
345
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#dehydrate-method)
|
|
342
346
|
* @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/external-data-loading#critical-dehydrationhydration)
|
|
343
347
|
*/
|
|
344
|
-
dehydrate?: () =>
|
|
345
|
-
TDehydrated,
|
|
346
|
-
ValidateSerializableInput<Register, TDehydrated>
|
|
348
|
+
dehydrate?: () => Awaitable<
|
|
349
|
+
Constrain<TDehydrated, ValidateSerializableInput<Register, TDehydrated>>
|
|
347
350
|
>
|
|
348
351
|
/**
|
|
349
352
|
* A function that will be called when the router is hydrated.
|
|
@@ -787,34 +790,47 @@ export type ClearCacheFn<TRouter extends AnyRouter> = (opts?: {
|
|
|
787
790
|
}) => void
|
|
788
791
|
|
|
789
792
|
export interface ServerSsr {
|
|
790
|
-
/**
|
|
791
|
-
* Injects HTML synchronously into the stream.
|
|
792
|
-
* Emits an onInjectedHtml event that listeners can handle.
|
|
793
|
-
* If no subscriber is listening, the HTML is buffered and can be retrieved via takeBufferedHtml().
|
|
794
|
-
*/
|
|
793
|
+
/** Framework-only: injects router-owned HTML into the SSR stream. */
|
|
795
794
|
injectHtml: (html: string) => void
|
|
796
|
-
/**
|
|
797
|
-
* Injects a script tag synchronously into the stream.
|
|
798
|
-
*/
|
|
795
|
+
/** Framework-only: injects a router-owned script tag into the SSR stream. */
|
|
799
796
|
injectScript: (script: string) => void
|
|
800
797
|
isDehydrated: () => boolean
|
|
801
798
|
isSerializationFinished: () => boolean
|
|
799
|
+
/** Framework-only: atomically reserves the pass-through stream path if safe. */
|
|
800
|
+
reserveStreamFastPath: () => boolean
|
|
801
|
+
/** Framework-only. */
|
|
802
|
+
onInjectedHtml: (listener: () => void) => () => void
|
|
803
|
+
/** Framework-only. */
|
|
802
804
|
onRenderFinished: (listener: () => void) => void
|
|
805
|
+
/** Framework-only. */
|
|
803
806
|
setRenderFinished: () => void
|
|
807
|
+
/** Framework-only. */
|
|
804
808
|
cleanup: () => void
|
|
805
|
-
onSerializationFinished: (listener: () => void) => void
|
|
806
|
-
dehydrate: (opts?: {
|
|
807
|
-
requestAssets?: Array<RouterManagedTag>
|
|
808
|
-
}) => Promise<void>
|
|
809
|
-
takeBufferedScripts: () => RouterManagedTag | undefined
|
|
810
809
|
/**
|
|
811
|
-
*
|
|
812
|
-
*
|
|
810
|
+
* Register a listener invoked when the SSR request lifecycle ends (success,
|
|
811
|
+
* error, abort, or stream lifetime expiry). Use to tear down per-request
|
|
812
|
+
* resources whose references would otherwise pin the router (e.g. query
|
|
813
|
+
* cache subscriptions, gcTime timers, abort controllers).
|
|
814
|
+
*
|
|
815
|
+
* Listeners run synchronously and exactly once. Errors are caught and logged.
|
|
813
816
|
*/
|
|
817
|
+
onCleanup: (listener: () => void) => void
|
|
818
|
+
/** Framework-only. */
|
|
819
|
+
onSerializationFinished: (listener: () => void) => () => void
|
|
820
|
+
/** Framework-only. */
|
|
821
|
+
dehydrate: (opts?: { requestAssets?: ManifestRouteAssets }) => Promise<void>
|
|
822
|
+
/** Framework-only. */
|
|
823
|
+
takeBufferedScripts: () => RouterManagedTag | undefined
|
|
824
|
+
/** Framework-only: takes buffered router-owned HTML. */
|
|
814
825
|
takeBufferedHtml: () => string | undefined
|
|
826
|
+
/** Framework-only. */
|
|
815
827
|
liftScriptBarrier: () => void
|
|
816
828
|
}
|
|
817
829
|
|
|
830
|
+
export interface RouterSsrLifecycle {
|
|
831
|
+
onServerSsrAttach?: Array<(serverSsr: ServerSsr) => void>
|
|
832
|
+
}
|
|
833
|
+
|
|
818
834
|
export type AnyRouterWithContext<TContext> = RouterCore<
|
|
819
835
|
AnyRouteWithContext<TContext>,
|
|
820
836
|
any,
|
|
@@ -2975,6 +2991,8 @@ export class RouterCore<
|
|
|
2975
2991
|
|
|
2976
2992
|
serverSsr?: ServerSsr
|
|
2977
2993
|
|
|
2994
|
+
serverSsrLifecycle?: RouterSsrLifecycle
|
|
2995
|
+
|
|
2978
2996
|
hasNotFoundMatch = () => {
|
|
2979
2997
|
return this.stores.matches
|
|
2980
2998
|
.get()
|
|
@@ -5,10 +5,11 @@ import {
|
|
|
5
5
|
getNormalizedURL,
|
|
6
6
|
getOrigin,
|
|
7
7
|
} from './ssr-server'
|
|
8
|
+
import { normalizeSsrResponse } from './handlerCallback'
|
|
8
9
|
import type { HandlerCallback } from './handlerCallback'
|
|
9
10
|
import type { AnyHeaders } from './headers'
|
|
10
11
|
import type { AnyRouter } from '../router'
|
|
11
|
-
import type {
|
|
12
|
+
import type { ServerManifest } from '../manifest'
|
|
12
13
|
|
|
13
14
|
export type RequestHandler<TRouter extends AnyRouter> = (
|
|
14
15
|
cb: HandlerCallback<TRouter>,
|
|
@@ -21,12 +22,11 @@ export function createRequestHandler<TRouter extends AnyRouter>({
|
|
|
21
22
|
}: {
|
|
22
23
|
createRouter: () => TRouter
|
|
23
24
|
request: Request
|
|
24
|
-
getRouterManifest?: () =>
|
|
25
|
+
getRouterManifest?: () => ServerManifest | Promise<ServerManifest>
|
|
25
26
|
}): RequestHandler<TRouter> {
|
|
26
27
|
return async (cb) => {
|
|
27
28
|
const router = createRouter()
|
|
28
|
-
|
|
29
|
-
let cbWillCleanup = false
|
|
29
|
+
let responseOwnsCleanup = false
|
|
30
30
|
|
|
31
31
|
try {
|
|
32
32
|
attachRouterServerSsrUtils({
|
|
@@ -58,19 +58,19 @@ export function createRequestHandler<TRouter extends AnyRouter>({
|
|
|
58
58
|
router,
|
|
59
59
|
})
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
cbWillCleanup = true
|
|
63
|
-
return cb({
|
|
61
|
+
const response = await cb({
|
|
64
62
|
request,
|
|
65
63
|
router,
|
|
66
64
|
responseHeaders,
|
|
67
65
|
})
|
|
66
|
+
const ssrResponse = normalizeSsrResponse(response)
|
|
67
|
+
responseOwnsCleanup = ssrResponse.serverSsrCleanup === 'stream'
|
|
68
|
+
return ssrResponse.response
|
|
68
69
|
} finally {
|
|
69
|
-
if (!
|
|
70
|
+
if (!responseOwnsCleanup) {
|
|
70
71
|
// Clean up router SSR state if the callback won't handle it
|
|
71
72
|
// (e.g., if an error occurred before the callback was invoked).
|
|
72
|
-
//
|
|
73
|
-
// for streaming, or directly in renderRouterToString for non-streaming).
|
|
73
|
+
// Transformed streaming response bodies clean up when consumed/cancelled.
|
|
74
74
|
router.serverSsr?.cleanup()
|
|
75
75
|
}
|
|
76
76
|
}
|
|
@@ -78,9 +78,10 @@ export function createRequestHandler<TRouter extends AnyRouter>({
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
function getRequestHeaders(opts: { router: AnyRouter }): Headers {
|
|
81
|
-
const matchHeaders =
|
|
82
|
-
|
|
83
|
-
.
|
|
81
|
+
const matchHeaders: Array<AnyHeaders> = []
|
|
82
|
+
for (const match of opts.router.stores.matches.get()) {
|
|
83
|
+
matchHeaders.push(match.headers)
|
|
84
|
+
}
|
|
84
85
|
|
|
85
86
|
// Handle Redirects
|
|
86
87
|
const redirect = opts.router.stores.redirect.get()
|
|
@@ -1,11 +1,94 @@
|
|
|
1
1
|
import type { AnyRouter } from '../router'
|
|
2
2
|
|
|
3
|
+
export type SsrResponse =
|
|
4
|
+
| {
|
|
5
|
+
response: Response
|
|
6
|
+
serverSsrCleanup: 'none'
|
|
7
|
+
}
|
|
8
|
+
| {
|
|
9
|
+
response: Response
|
|
10
|
+
serverSsrCleanup: 'stream'
|
|
11
|
+
dispose: (reason?: unknown) => Promise<void>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type HandlerCallbackResult = Response | SsrResponse
|
|
15
|
+
|
|
16
|
+
export function isSsrResponse(value: unknown): value is SsrResponse {
|
|
17
|
+
return (
|
|
18
|
+
typeof value === 'object' &&
|
|
19
|
+
value !== null &&
|
|
20
|
+
'response' in value &&
|
|
21
|
+
'serverSsrCleanup' in value
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function normalizeSsrResponse(
|
|
26
|
+
result: HandlerCallbackResult,
|
|
27
|
+
): SsrResponse {
|
|
28
|
+
return isSsrResponse(result)
|
|
29
|
+
? result
|
|
30
|
+
: { response: result, serverSsrCleanup: 'none' }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function createSsrStreamResponse<TRouter extends AnyRouter>(
|
|
34
|
+
router: TRouter,
|
|
35
|
+
response: Response,
|
|
36
|
+
): SsrResponse {
|
|
37
|
+
if (!response.body) {
|
|
38
|
+
throw new Error('Invariant failed: SSR stream response requires a body')
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
let disposed = false
|
|
42
|
+
return {
|
|
43
|
+
response,
|
|
44
|
+
serverSsrCleanup: 'stream',
|
|
45
|
+
async dispose(reason?: unknown) {
|
|
46
|
+
if (disposed) return
|
|
47
|
+
disposed = true
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
await response.body!.cancel(reason)
|
|
51
|
+
} catch {
|
|
52
|
+
// ignore; fallback cleanup below still releases router SSR state
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
router.serverSsr?.cleanup()
|
|
56
|
+
},
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export async function replaceSsrResponse(
|
|
61
|
+
result: HandlerCallbackResult,
|
|
62
|
+
response: Response,
|
|
63
|
+
reason?: unknown,
|
|
64
|
+
): Promise<SsrResponse> {
|
|
65
|
+
const ssrResponse = normalizeSsrResponse(result)
|
|
66
|
+
if (ssrResponse.serverSsrCleanup === 'stream') {
|
|
67
|
+
await ssrResponse.dispose(reason)
|
|
68
|
+
}
|
|
69
|
+
return { response, serverSsrCleanup: 'none' }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export async function stripSsrResponseBody(
|
|
73
|
+
result: HandlerCallbackResult,
|
|
74
|
+
reason?: unknown,
|
|
75
|
+
): Promise<SsrResponse> {
|
|
76
|
+
const ssrResponse = normalizeSsrResponse(result)
|
|
77
|
+
if (ssrResponse.serverSsrCleanup === 'stream') {
|
|
78
|
+
await ssrResponse.dispose(reason)
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
response: new Response(null, ssrResponse.response),
|
|
82
|
+
serverSsrCleanup: 'none',
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
3
86
|
export interface HandlerCallback<TRouter extends AnyRouter> {
|
|
4
87
|
(ctx: {
|
|
5
88
|
request: Request
|
|
6
89
|
router: TRouter
|
|
7
90
|
responseHeaders: Headers
|
|
8
|
-
}):
|
|
91
|
+
}): HandlerCallbackResult | Promise<HandlerCallbackResult>
|
|
9
92
|
}
|
|
10
93
|
|
|
11
94
|
export function defineHandlerCallback<TRouter extends AnyRouter>(
|
package/src/ssr/server.ts
CHANGED
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
export { createRequestHandler } from './createRequestHandler'
|
|
2
2
|
export type { RequestHandler } from './createRequestHandler'
|
|
3
|
-
export {
|
|
4
|
-
|
|
3
|
+
export {
|
|
4
|
+
createSsrStreamResponse,
|
|
5
|
+
defineHandlerCallback,
|
|
6
|
+
isSsrResponse,
|
|
7
|
+
normalizeSsrResponse,
|
|
8
|
+
replaceSsrResponse,
|
|
9
|
+
stripSsrResponseBody,
|
|
10
|
+
} from './handlerCallback'
|
|
11
|
+
export type {
|
|
12
|
+
HandlerCallback,
|
|
13
|
+
HandlerCallbackResult,
|
|
14
|
+
SsrResponse,
|
|
15
|
+
} from './handlerCallback'
|
|
5
16
|
export {
|
|
6
17
|
transformPipeableStreamWithRouter,
|
|
7
18
|
transformStreamWithRouter,
|
|
8
19
|
transformReadableStreamWithRouter,
|
|
9
20
|
} from './transformStreamWithRouter'
|
|
21
|
+
export type { TransformStreamWithRouterOptions } from './transformStreamWithRouter'
|
|
10
22
|
export {
|
|
11
23
|
attachRouterServerSsrUtils,
|
|
12
24
|
getNormalizedURL,
|