@tanstack/router-core 1.125.4 → 1.127.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 +1 -1
- package/dist/cjs/RouterProvider.d.cts +1 -0
- package/dist/cjs/index.d.cts +1 -2
- package/dist/cjs/router.cjs +26 -33
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/router.d.cts +10 -57
- package/dist/cjs/ssr/client.cjs +0 -2
- package/dist/cjs/ssr/client.cjs.map +1 -1
- package/dist/cjs/ssr/client.d.cts +1 -2
- package/dist/cjs/ssr/createRequestHandler.cjs +2 -1
- package/dist/cjs/ssr/createRequestHandler.cjs.map +1 -1
- package/dist/cjs/ssr/seroval-plugins.cjs +34 -0
- package/dist/cjs/ssr/seroval-plugins.cjs.map +1 -0
- package/dist/cjs/ssr/seroval-plugins.d.cts +10 -0
- package/dist/cjs/ssr/server.cjs +0 -4
- package/dist/cjs/ssr/server.cjs.map +1 -1
- package/dist/cjs/ssr/server.d.cts +1 -3
- package/dist/cjs/ssr/ssr-client.cjs +18 -56
- package/dist/cjs/ssr/ssr-client.cjs.map +1 -1
- package/dist/cjs/ssr/ssr-client.d.cts +17 -57
- package/dist/cjs/ssr/ssr-server.cjs +75 -220
- package/dist/cjs/ssr/ssr-server.cjs.map +1 -1
- package/dist/cjs/ssr/ssr-server.d.cts +14 -28
- package/dist/cjs/ssr/transformStreamWithRouter.cjs +1 -0
- package/dist/cjs/ssr/transformStreamWithRouter.cjs.map +1 -1
- package/dist/cjs/ssr/tsrScript.cjs +1 -1
- package/dist/cjs/ssr/tsrScript.cjs.map +1 -1
- package/dist/cjs/ssr/tsrScript.d.cts +0 -1
- package/dist/esm/Matches.d.ts +1 -1
- package/dist/esm/Matches.js.map +1 -1
- package/dist/esm/RouterProvider.d.ts +1 -0
- package/dist/esm/index.d.ts +1 -2
- package/dist/esm/router.d.ts +10 -57
- package/dist/esm/router.js +26 -33
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/ssr/client.d.ts +1 -2
- package/dist/esm/ssr/client.js +1 -3
- package/dist/esm/ssr/client.js.map +1 -1
- package/dist/esm/ssr/createRequestHandler.js +3 -2
- package/dist/esm/ssr/createRequestHandler.js.map +1 -1
- package/dist/esm/ssr/seroval-plugins.d.ts +10 -0
- package/dist/esm/ssr/seroval-plugins.js +34 -0
- package/dist/esm/ssr/seroval-plugins.js.map +1 -0
- package/dist/esm/ssr/server.d.ts +1 -3
- package/dist/esm/ssr/server.js +1 -5
- package/dist/esm/ssr/ssr-client.d.ts +17 -57
- package/dist/esm/ssr/ssr-client.js +18 -56
- package/dist/esm/ssr/ssr-client.js.map +1 -1
- package/dist/esm/ssr/ssr-server.d.ts +14 -28
- package/dist/esm/ssr/ssr-server.js +76 -221
- package/dist/esm/ssr/ssr-server.js.map +1 -1
- package/dist/esm/ssr/transformStreamWithRouter.js +1 -0
- package/dist/esm/ssr/transformStreamWithRouter.js.map +1 -1
- package/dist/esm/ssr/tsrScript.d.ts +0 -1
- package/dist/esm/ssr/tsrScript.js +1 -1
- package/dist/esm/ssr/tsrScript.js.map +1 -1
- package/package.json +3 -1
- package/src/Matches.ts +1 -1
- package/src/RouterProvider.ts +1 -0
- package/src/index.ts +0 -18
- package/src/router.ts +41 -89
- package/src/ssr/client.ts +1 -11
- package/src/ssr/createRequestHandler.ts +2 -2
- package/src/ssr/seroval-plugins.ts +43 -0
- package/src/ssr/server.ts +1 -14
- package/src/ssr/ssr-client.ts +35 -128
- package/src/ssr/ssr-server.ts +89 -307
- package/src/ssr/transformStreamWithRouter.ts +1 -0
- package/src/ssr/tsrScript.ts +4 -88
- package/dist/cjs/serializer.cjs +0 -146
- package/dist/cjs/serializer.cjs.map +0 -1
- package/dist/cjs/serializer.d.cts +0 -28
- package/dist/esm/serializer.d.ts +0 -28
- package/dist/esm/serializer.js +0 -146
- package/dist/esm/serializer.js.map +0 -1
- package/src/serializer.ts +0 -205
package/src/ssr/ssr-server.ts
CHANGED
|
@@ -1,37 +1,50 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import { tsrSerializer } from '../serializer'
|
|
1
|
+
import { crossSerializeStream, getCrossReferenceHeader } from 'seroval'
|
|
2
|
+
import { ReadableStreamPlugin } from 'seroval-plugins/web'
|
|
3
|
+
import invariant from 'tiny-invariant'
|
|
4
|
+
import { createControlledPromise } from '../utils'
|
|
6
5
|
import minifiedTsrBootStrapScript from './tsrScript?script-string'
|
|
7
|
-
import
|
|
8
|
-
import type {
|
|
9
|
-
ClientExtractedBaseEntry,
|
|
10
|
-
DehydratedRouter,
|
|
11
|
-
ResolvePromiseState,
|
|
12
|
-
SsrMatch,
|
|
13
|
-
} from './ssr-client'
|
|
6
|
+
import { ShallowErrorPlugin } from './seroval-plugins'
|
|
14
7
|
import type { AnyRouter } from '../router'
|
|
15
|
-
import type {
|
|
8
|
+
import type { DehydratedMatch } from './ssr-client'
|
|
9
|
+
import type { DehydratedRouter } from './client'
|
|
16
10
|
import type { AnyRouteMatch } from '../Matches'
|
|
11
|
+
import type { Manifest } from '../manifest'
|
|
17
12
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
13
|
+
declare module '../router' {
|
|
14
|
+
interface ServerSsr {
|
|
15
|
+
setRenderFinished: () => void
|
|
16
|
+
}
|
|
17
|
+
interface RouterEvents {
|
|
18
|
+
onInjectedHtml: {
|
|
19
|
+
type: 'onInjectedHtml'
|
|
20
|
+
promise: Promise<string>
|
|
21
|
+
}
|
|
22
|
+
}
|
|
25
23
|
}
|
|
26
24
|
|
|
27
|
-
export
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
export const GLOBAL_TSR = '$_TSR'
|
|
26
|
+
const SCOPE_ID = 'tsr'
|
|
27
|
+
|
|
28
|
+
export function dehydrateMatch(match: AnyRouteMatch): DehydratedMatch {
|
|
29
|
+
const dehydratedMatch: DehydratedMatch = {
|
|
30
|
+
i: match.id,
|
|
31
|
+
u: match.updatedAt,
|
|
32
|
+
s: match.status,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const properties = [
|
|
36
|
+
['__beforeLoadContext', 'b'],
|
|
37
|
+
['loaderData', 'l'],
|
|
38
|
+
['error', 'e'],
|
|
39
|
+
['ssr', 'ssr'],
|
|
40
|
+
] as const
|
|
31
41
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
42
|
+
for (const [key, shorthand] of properties) {
|
|
43
|
+
if (match[key] !== undefined) {
|
|
44
|
+
dehydratedMatch[shorthand] = match[key]
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return dehydratedMatch
|
|
35
48
|
}
|
|
36
49
|
|
|
37
50
|
export function attachRouterServerSsrUtils(
|
|
@@ -40,12 +53,22 @@ export function attachRouterServerSsrUtils(
|
|
|
40
53
|
) {
|
|
41
54
|
router.ssr = {
|
|
42
55
|
manifest,
|
|
43
|
-
serializer: tsrSerializer,
|
|
44
56
|
}
|
|
57
|
+
const serializationRefs = new Map<unknown, number>()
|
|
58
|
+
|
|
59
|
+
let initialScriptSent = false
|
|
60
|
+
const getInitialScript = () => {
|
|
61
|
+
if (initialScriptSent) {
|
|
62
|
+
return ''
|
|
63
|
+
}
|
|
64
|
+
initialScriptSent = true
|
|
65
|
+
return `${getCrossReferenceHeader(SCOPE_ID)};${minifiedTsrBootStrapScript};`
|
|
66
|
+
}
|
|
67
|
+
let _dehydrated = false
|
|
68
|
+
const listeners: Array<() => void> = []
|
|
45
69
|
|
|
46
70
|
router.serverSsr = {
|
|
47
71
|
injectedHtml: [],
|
|
48
|
-
streamedKeys: new Set(),
|
|
49
72
|
injectHtml: (getHtml) => {
|
|
50
73
|
const promise = Promise.resolve().then(getHtml)
|
|
51
74
|
router.serverSsr!.injectedHtml.push(promise)
|
|
@@ -56,291 +79,50 @@ export function attachRouterServerSsrUtils(
|
|
|
56
79
|
|
|
57
80
|
return promise.then(() => {})
|
|
58
81
|
},
|
|
59
|
-
injectScript: (getScript
|
|
82
|
+
injectScript: (getScript) => {
|
|
60
83
|
return router.serverSsr!.injectHtml(async () => {
|
|
61
84
|
const script = await getScript()
|
|
62
|
-
return `<script class='tsr
|
|
63
|
-
process.env.NODE_ENV === 'development' && (opts?.logScript ?? true)
|
|
64
|
-
? `; console.info(\`Injected From Server:
|
|
65
|
-
${jsesc(script, { quotes: 'backtick' })}\`)`
|
|
66
|
-
: ''
|
|
67
|
-
}; if (typeof __TSR_SSR__ !== 'undefined') __TSR_SSR__.cleanScripts()</script>`
|
|
85
|
+
return `<script class='$tsr'>${getInitialScript()}${script};if (typeof $_TSR !== 'undefined') $_TSR.c()</script>`
|
|
68
86
|
})
|
|
69
87
|
},
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
'Key has already been streamed: ' + key,
|
|
74
|
-
)
|
|
88
|
+
dehydrate: async () => {
|
|
89
|
+
invariant(!_dehydrated, 'router is already dehydrated!')
|
|
90
|
+
const matches = router.state.matches.map(dehydrateMatch)
|
|
75
91
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
`__TSR_SSR__.streamedValues['${key}'] = { value: ${jsesc(
|
|
80
|
-
router.ssr!.serializer.stringify(value),
|
|
81
|
-
{
|
|
82
|
-
isScriptContext: true,
|
|
83
|
-
wrap: true,
|
|
84
|
-
json: true,
|
|
85
|
-
},
|
|
86
|
-
)}}`,
|
|
87
|
-
)
|
|
88
|
-
},
|
|
89
|
-
onMatchSettled,
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
router.serverSsr.injectScript(() => minifiedTsrBootStrapScript, {
|
|
93
|
-
logScript: false,
|
|
94
|
-
})
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export function dehydrateRouter(router: AnyRouter) {
|
|
98
|
-
const dehydratedRouter: DehydratedRouter = {
|
|
99
|
-
manifest: router.ssr!.manifest,
|
|
100
|
-
dehydratedData: router.options.dehydrate?.(),
|
|
101
|
-
lastMatchId:
|
|
102
|
-
router.state.matches[router.state.matches.length - 1]?.id || '',
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
router.serverSsr!.injectScript(
|
|
106
|
-
() =>
|
|
107
|
-
`__TSR_SSR__.dehydrated = ${jsesc(
|
|
108
|
-
router.ssr!.serializer.stringify(dehydratedRouter),
|
|
109
|
-
{
|
|
110
|
-
isScriptContext: true,
|
|
111
|
-
wrap: true,
|
|
112
|
-
json: true,
|
|
113
|
-
},
|
|
114
|
-
)}`,
|
|
115
|
-
)
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export function extractAsyncLoaderData(
|
|
119
|
-
loaderData: any,
|
|
120
|
-
ctx: {
|
|
121
|
-
match: AnyRouteMatch
|
|
122
|
-
router: AnyRouter
|
|
123
|
-
},
|
|
124
|
-
) {
|
|
125
|
-
const extracted: Array<ServerExtractedEntry> = []
|
|
126
|
-
|
|
127
|
-
const replaced = replaceBy(loaderData, (value, path) => {
|
|
128
|
-
// If it's a stream, we need to tee it so we can read it multiple times
|
|
129
|
-
if (value instanceof ReadableStream) {
|
|
130
|
-
const [copy1, copy2] = value.tee()
|
|
131
|
-
const entry: ServerExtractedStream = {
|
|
132
|
-
type: 'stream',
|
|
133
|
-
path,
|
|
134
|
-
id: extracted.length,
|
|
135
|
-
matchIndex: ctx.match.index,
|
|
136
|
-
stream: copy2,
|
|
92
|
+
const dehydratedRouter: DehydratedRouter = {
|
|
93
|
+
manifest: router.ssr!.manifest,
|
|
94
|
+
matches,
|
|
137
95
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
const deferredPromise = defer(value)
|
|
143
|
-
const entry: ServerExtractedPromise = {
|
|
144
|
-
type: 'promise',
|
|
145
|
-
path,
|
|
146
|
-
id: extracted.length,
|
|
147
|
-
matchIndex: ctx.match.index,
|
|
148
|
-
promise: deferredPromise,
|
|
96
|
+
const lastMatchId =
|
|
97
|
+
router.state.matches[router.state.matches.length - 1]?.id
|
|
98
|
+
if (lastMatchId) {
|
|
99
|
+
dehydratedRouter.lastMatchId = lastMatchId
|
|
149
100
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
match: AnyRouteMatch
|
|
162
|
-
}) {
|
|
163
|
-
const { router, match } = opts
|
|
164
|
-
|
|
165
|
-
let extracted: Array<ServerExtractedEntry> | undefined = undefined
|
|
166
|
-
let serializedLoaderData: any = undefined
|
|
167
|
-
if (match.loaderData !== undefined) {
|
|
168
|
-
const result = extractAsyncLoaderData(match.loaderData, {
|
|
169
|
-
router,
|
|
170
|
-
match,
|
|
171
|
-
})
|
|
172
|
-
match.loaderData = result.replaced
|
|
173
|
-
extracted = result.extracted
|
|
174
|
-
serializedLoaderData = extracted.reduce(
|
|
175
|
-
(acc: any, entry: ServerExtractedEntry) => {
|
|
176
|
-
return deepImmutableSetByPath(acc, ['temp', ...entry.path], undefined)
|
|
177
|
-
},
|
|
178
|
-
{ temp: result.replaced },
|
|
179
|
-
).temp
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const initCode = `__TSR_SSR__.initMatch(${jsesc(
|
|
183
|
-
{
|
|
184
|
-
id: match.id,
|
|
185
|
-
__beforeLoadContext: router.ssr!.serializer.stringify(
|
|
186
|
-
match.__beforeLoadContext,
|
|
187
|
-
),
|
|
188
|
-
loaderData: router.ssr!.serializer.stringify(serializedLoaderData),
|
|
189
|
-
error: router.ssr!.serializer.stringify(match.error),
|
|
190
|
-
extracted: extracted?.map((entry) => pick(entry, ['type', 'path'])),
|
|
191
|
-
updatedAt: match.updatedAt,
|
|
192
|
-
status: match.status,
|
|
193
|
-
ssr: match.ssr,
|
|
194
|
-
} satisfies SsrMatch,
|
|
195
|
-
{
|
|
196
|
-
isScriptContext: true,
|
|
197
|
-
wrap: true,
|
|
198
|
-
json: true,
|
|
199
|
-
},
|
|
200
|
-
)})`
|
|
201
|
-
|
|
202
|
-
router.serverSsr!.injectScript(() => initCode)
|
|
203
|
-
|
|
204
|
-
if (extracted) {
|
|
205
|
-
extracted.forEach((entry) => {
|
|
206
|
-
if (entry.type === 'promise') return injectPromise(entry)
|
|
207
|
-
return injectStream(entry)
|
|
208
|
-
})
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
function injectPromise(entry: ServerExtractedPromise) {
|
|
212
|
-
router.serverSsr!.injectScript(async () => {
|
|
213
|
-
await entry.promise
|
|
214
|
-
|
|
215
|
-
return `__TSR_SSR__.resolvePromise(${jsesc(
|
|
216
|
-
{
|
|
217
|
-
matchId: match.id,
|
|
218
|
-
id: entry.id,
|
|
219
|
-
promiseState: entry.promise[TSR_DEFERRED_PROMISE],
|
|
220
|
-
} satisfies ResolvePromiseState,
|
|
221
|
-
{
|
|
222
|
-
isScriptContext: true,
|
|
223
|
-
wrap: true,
|
|
224
|
-
json: true,
|
|
101
|
+
dehydratedRouter.dehydratedData = await router.options.dehydrate?.()
|
|
102
|
+
_dehydrated = true
|
|
103
|
+
|
|
104
|
+
const p = createControlledPromise<string>()
|
|
105
|
+
crossSerializeStream(dehydratedRouter, {
|
|
106
|
+
refs: serializationRefs,
|
|
107
|
+
// TODO make plugins configurable
|
|
108
|
+
plugins: [ReadableStreamPlugin, ShallowErrorPlugin],
|
|
109
|
+
onSerialize: (data, initial) => {
|
|
110
|
+
const serialized = initial ? `${GLOBAL_TSR}["router"]=` + data : data
|
|
111
|
+
router.serverSsr!.injectScript(() => serialized)
|
|
225
112
|
},
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
const code = `__TSR_SSR__.injectChunk(${jsesc(
|
|
241
|
-
{
|
|
242
|
-
matchId: match.id,
|
|
243
|
-
id: entry.id,
|
|
244
|
-
chunk: chunk.value,
|
|
245
|
-
},
|
|
246
|
-
{
|
|
247
|
-
isScriptContext: true,
|
|
248
|
-
wrap: true,
|
|
249
|
-
json: true,
|
|
250
|
-
},
|
|
251
|
-
)})`
|
|
252
|
-
|
|
253
|
-
router.serverSsr!.injectScript(() => code)
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
router.serverSsr!.injectScript(
|
|
258
|
-
() =>
|
|
259
|
-
`__TSR_SSR__.closeStream(${jsesc(
|
|
260
|
-
{
|
|
261
|
-
matchId: match.id,
|
|
262
|
-
id: entry.id,
|
|
263
|
-
},
|
|
264
|
-
{
|
|
265
|
-
isScriptContext: true,
|
|
266
|
-
wrap: true,
|
|
267
|
-
json: true,
|
|
268
|
-
},
|
|
269
|
-
)})`,
|
|
270
|
-
)
|
|
271
|
-
} catch (err) {
|
|
272
|
-
console.error('stream read error', err)
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
return ''
|
|
276
|
-
})
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
function deepImmutableSetByPath<T>(obj: T, path: Array<string>, value: any): T {
|
|
281
|
-
// immutable set by path retaining array and object references
|
|
282
|
-
if (path.length === 0) {
|
|
283
|
-
return value
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
const [key, ...rest] = path
|
|
287
|
-
|
|
288
|
-
if (Array.isArray(obj)) {
|
|
289
|
-
return obj.map((item, i) => {
|
|
290
|
-
if (i === Number(key)) {
|
|
291
|
-
return deepImmutableSetByPath(item, rest, value)
|
|
292
|
-
}
|
|
293
|
-
return item
|
|
294
|
-
}) as T
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
if (isPlainObject(obj)) {
|
|
298
|
-
return {
|
|
299
|
-
...obj,
|
|
300
|
-
[key!]: deepImmutableSetByPath((obj as any)[key!], rest, value),
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
return obj
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
export function replaceBy<T>(
|
|
308
|
-
obj: T,
|
|
309
|
-
cb: (value: any, path: Array<string>) => any,
|
|
310
|
-
path: Array<string> = [],
|
|
311
|
-
): T {
|
|
312
|
-
if (isPlainArray(obj)) {
|
|
313
|
-
return obj.map((value, i) => replaceBy(value, cb, [...path, `${i}`])) as any
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
if (isPlainObject(obj)) {
|
|
317
|
-
// Do not allow objects with illegal
|
|
318
|
-
const newObj: any = {}
|
|
319
|
-
|
|
320
|
-
for (const key in obj) {
|
|
321
|
-
newObj[key] = replaceBy(obj[key], cb, [...path, key])
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
return newObj
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
// // Detect classes, functions, and other non-serializable objects
|
|
328
|
-
// // and return undefined. Exclude some known types that are serializable
|
|
329
|
-
// if (
|
|
330
|
-
// typeof obj === 'function' ||
|
|
331
|
-
// (typeof obj === 'object' &&
|
|
332
|
-
// ![Object, Promise, ReadableStream].includes((obj as any)?.constructor))
|
|
333
|
-
// ) {
|
|
334
|
-
// console.info(obj)
|
|
335
|
-
// warning(false, `Non-serializable value ☝️ found at ${path.join('.')}`)
|
|
336
|
-
// return undefined as any
|
|
337
|
-
// }
|
|
338
|
-
|
|
339
|
-
const newObj = cb(obj, path)
|
|
340
|
-
|
|
341
|
-
if (newObj !== obj) {
|
|
342
|
-
return newObj
|
|
113
|
+
scopeId: SCOPE_ID,
|
|
114
|
+
onDone: () => p.resolve(''),
|
|
115
|
+
onError: (err) => p.reject(err),
|
|
116
|
+
})
|
|
117
|
+
// make sure the stream is kept open until the promise is resolved
|
|
118
|
+
router.serverSsr!.injectHtml(() => p)
|
|
119
|
+
},
|
|
120
|
+
isDehydrated() {
|
|
121
|
+
return _dehydrated
|
|
122
|
+
},
|
|
123
|
+
onRenderFinished: (listener) => listeners.push(listener),
|
|
124
|
+
setRenderFinished: () => {
|
|
125
|
+
listeners.forEach((l) => l())
|
|
126
|
+
},
|
|
343
127
|
}
|
|
344
|
-
|
|
345
|
-
return obj
|
|
346
128
|
}
|
|
@@ -242,6 +242,7 @@ export function transformStreamWithRouter(
|
|
|
242
242
|
onEnd: () => {
|
|
243
243
|
// Mark the app as done rendering
|
|
244
244
|
isAppRendering = false
|
|
245
|
+
router.serverSsr!.setRenderFinished()
|
|
245
246
|
|
|
246
247
|
// If there are no pending promises, resolve the injectedHtmlDonePromise
|
|
247
248
|
if (processingCount === 0) {
|
package/src/ssr/tsrScript.ts
CHANGED
|
@@ -1,91 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
matches: [],
|
|
6
|
-
streamedValues: {},
|
|
7
|
-
initMatch: (match) => {
|
|
8
|
-
__TSR_SSR__.matches.push(match)
|
|
9
|
-
|
|
10
|
-
match.extracted?.forEach((ex) => {
|
|
11
|
-
if (ex.type === 'stream') {
|
|
12
|
-
let controller
|
|
13
|
-
ex.value = new ReadableStream({
|
|
14
|
-
start(c) {
|
|
15
|
-
controller = {
|
|
16
|
-
enqueue: (chunk: unknown) => {
|
|
17
|
-
try {
|
|
18
|
-
c.enqueue(chunk)
|
|
19
|
-
} catch {}
|
|
20
|
-
},
|
|
21
|
-
close: () => {
|
|
22
|
-
try {
|
|
23
|
-
c.close()
|
|
24
|
-
} catch {}
|
|
25
|
-
},
|
|
26
|
-
}
|
|
27
|
-
},
|
|
28
|
-
})
|
|
29
|
-
ex.value.controller = controller
|
|
30
|
-
} else {
|
|
31
|
-
let resolve: ControllablePromise['reject'] | undefined
|
|
32
|
-
let reject: ControllablePromise['reject'] | undefined
|
|
33
|
-
|
|
34
|
-
ex.value = new Promise((_resolve, _reject) => {
|
|
35
|
-
reject = _reject
|
|
36
|
-
resolve = _resolve
|
|
37
|
-
}) as ControllablePromise
|
|
38
|
-
ex.value.reject = reject!
|
|
39
|
-
ex.value.resolve = resolve!
|
|
40
|
-
}
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
return true
|
|
44
|
-
},
|
|
45
|
-
resolvePromise: ({ matchId, id, promiseState }) => {
|
|
46
|
-
const match = __TSR_SSR__.matches.find((m) => m.id === matchId)
|
|
47
|
-
if (match) {
|
|
48
|
-
const ex = match.extracted?.[id]
|
|
49
|
-
if (
|
|
50
|
-
ex &&
|
|
51
|
-
ex.type === 'promise' &&
|
|
52
|
-
ex.value &&
|
|
53
|
-
promiseState.status === 'success'
|
|
54
|
-
) {
|
|
55
|
-
ex.value.resolve(promiseState.data)
|
|
56
|
-
return true
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
return false
|
|
60
|
-
},
|
|
61
|
-
injectChunk: ({ matchId, id, chunk }) => {
|
|
62
|
-
const match = __TSR_SSR__.matches.find((m) => m.id === matchId)
|
|
63
|
-
|
|
64
|
-
if (match) {
|
|
65
|
-
const ex = match.extracted?.[id]
|
|
66
|
-
if (ex && ex.type === 'stream' && ex.value?.controller) {
|
|
67
|
-
ex.value.controller.enqueue(new TextEncoder().encode(chunk.toString()))
|
|
68
|
-
return true
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
return false
|
|
72
|
-
},
|
|
73
|
-
closeStream: ({ matchId, id }) => {
|
|
74
|
-
const match = __TSR_SSR__.matches.find((m) => m.id === matchId)
|
|
75
|
-
if (match) {
|
|
76
|
-
const ex = match.extracted?.[id]
|
|
77
|
-
if (ex && ex.type === 'stream' && ex.value?.controller) {
|
|
78
|
-
ex.value.controller.close()
|
|
79
|
-
return true
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
return false
|
|
83
|
-
},
|
|
84
|
-
cleanScripts: () => {
|
|
85
|
-
document.querySelectorAll('.tsr-once').forEach((el) => {
|
|
86
|
-
el.remove()
|
|
1
|
+
self.$_TSR = {
|
|
2
|
+
c: () => {
|
|
3
|
+
document.querySelectorAll('.\\$tsr').forEach((o) => {
|
|
4
|
+
o.remove()
|
|
87
5
|
})
|
|
88
6
|
},
|
|
89
7
|
}
|
|
90
|
-
|
|
91
|
-
window.__TSR_SSR__ = __TSR_SSR__
|
package/dist/cjs/serializer.cjs
DELETED
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const utils = require("./utils.cjs");
|
|
4
|
-
const tsrSerializer = {
|
|
5
|
-
stringify: (value) => JSON.stringify(value, function replacer(key, val) {
|
|
6
|
-
const ogVal = this[key];
|
|
7
|
-
const serializer = serializers.find((t) => t.stringifyCondition(ogVal));
|
|
8
|
-
if (serializer) {
|
|
9
|
-
return serializer.stringify(ogVal);
|
|
10
|
-
}
|
|
11
|
-
return val;
|
|
12
|
-
}),
|
|
13
|
-
parse: (value) => JSON.parse(value, function parser(key, val) {
|
|
14
|
-
const ogVal = this[key];
|
|
15
|
-
if (utils.isPlainObject(ogVal)) {
|
|
16
|
-
const serializer = serializers.find((t) => t.parseCondition(ogVal));
|
|
17
|
-
if (serializer) {
|
|
18
|
-
return serializer.parse(ogVal);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
return val;
|
|
22
|
-
}),
|
|
23
|
-
encode: (value) => {
|
|
24
|
-
if (Array.isArray(value)) {
|
|
25
|
-
return value.map((v) => tsrSerializer.encode(v));
|
|
26
|
-
}
|
|
27
|
-
if (utils.isPlainObject(value)) {
|
|
28
|
-
return Object.fromEntries(
|
|
29
|
-
Object.entries(value).map(([key, v]) => [key, tsrSerializer.encode(v)])
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
const serializer = serializers.find((t) => t.stringifyCondition(value));
|
|
33
|
-
if (serializer) {
|
|
34
|
-
return serializer.stringify(value);
|
|
35
|
-
}
|
|
36
|
-
return value;
|
|
37
|
-
},
|
|
38
|
-
decode: (value) => {
|
|
39
|
-
if (utils.isPlainObject(value)) {
|
|
40
|
-
const serializer = serializers.find((t) => t.parseCondition(value));
|
|
41
|
-
if (serializer) {
|
|
42
|
-
return serializer.parse(value);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
if (Array.isArray(value)) {
|
|
46
|
-
return value.map((v) => tsrSerializer.decode(v));
|
|
47
|
-
}
|
|
48
|
-
if (utils.isPlainObject(value)) {
|
|
49
|
-
return Object.fromEntries(
|
|
50
|
-
Object.entries(value).map(([key, v]) => [key, tsrSerializer.decode(v)])
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
return value;
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
const createSerializer = (key, check, toValue, fromValue) => ({
|
|
57
|
-
key,
|
|
58
|
-
stringifyCondition: check,
|
|
59
|
-
stringify: (value) => ({ [`$${key}`]: toValue(value) }),
|
|
60
|
-
parseCondition: (value) => Object.hasOwn(value, `$${key}`),
|
|
61
|
-
parse: (value) => fromValue(value[`$${key}`])
|
|
62
|
-
});
|
|
63
|
-
const serializers = [
|
|
64
|
-
createSerializer(
|
|
65
|
-
// Key
|
|
66
|
-
"undefined",
|
|
67
|
-
// Check
|
|
68
|
-
(v) => v === void 0,
|
|
69
|
-
// To
|
|
70
|
-
() => 0,
|
|
71
|
-
// From
|
|
72
|
-
() => void 0
|
|
73
|
-
),
|
|
74
|
-
createSerializer(
|
|
75
|
-
// Key
|
|
76
|
-
"date",
|
|
77
|
-
// Check
|
|
78
|
-
(v) => v instanceof Date,
|
|
79
|
-
// To
|
|
80
|
-
(v) => v.toISOString(),
|
|
81
|
-
// From
|
|
82
|
-
(v) => new Date(v)
|
|
83
|
-
),
|
|
84
|
-
createSerializer(
|
|
85
|
-
// Key
|
|
86
|
-
"error",
|
|
87
|
-
// Check
|
|
88
|
-
(v) => v instanceof Error,
|
|
89
|
-
// To
|
|
90
|
-
(v) => ({
|
|
91
|
-
...v,
|
|
92
|
-
message: v.message,
|
|
93
|
-
stack: process.env.NODE_ENV === "development" ? v.stack : void 0,
|
|
94
|
-
cause: v.cause
|
|
95
|
-
}),
|
|
96
|
-
// From
|
|
97
|
-
(v) => Object.assign(new Error(v.message), v)
|
|
98
|
-
),
|
|
99
|
-
createSerializer(
|
|
100
|
-
// Key
|
|
101
|
-
"formData",
|
|
102
|
-
// Check
|
|
103
|
-
(v) => v instanceof FormData,
|
|
104
|
-
// To
|
|
105
|
-
(v) => {
|
|
106
|
-
const entries = {};
|
|
107
|
-
v.forEach((value, key) => {
|
|
108
|
-
const entry = entries[key];
|
|
109
|
-
if (entry !== void 0) {
|
|
110
|
-
if (Array.isArray(entry)) {
|
|
111
|
-
entry.push(value);
|
|
112
|
-
} else {
|
|
113
|
-
entries[key] = [entry, value];
|
|
114
|
-
}
|
|
115
|
-
} else {
|
|
116
|
-
entries[key] = value;
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
return entries;
|
|
120
|
-
},
|
|
121
|
-
// From
|
|
122
|
-
(v) => {
|
|
123
|
-
const formData = new FormData();
|
|
124
|
-
Object.entries(v).forEach(([key, value]) => {
|
|
125
|
-
if (Array.isArray(value)) {
|
|
126
|
-
value.forEach((val) => formData.append(key, val));
|
|
127
|
-
} else {
|
|
128
|
-
formData.append(key, value);
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
return formData;
|
|
132
|
-
}
|
|
133
|
-
),
|
|
134
|
-
createSerializer(
|
|
135
|
-
// Key
|
|
136
|
-
"bigint",
|
|
137
|
-
// Check
|
|
138
|
-
(v) => typeof v === "bigint",
|
|
139
|
-
// To
|
|
140
|
-
(v) => v.toString(),
|
|
141
|
-
// From
|
|
142
|
-
(v) => BigInt(v)
|
|
143
|
-
)
|
|
144
|
-
];
|
|
145
|
-
exports.tsrSerializer = tsrSerializer;
|
|
146
|
-
//# sourceMappingURL=serializer.cjs.map
|