@tanstack/router-core 1.168.9 → 1.168.10
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/hash-scroll.cjs +1 -1
- package/dist/cjs/hash-scroll.cjs.map +1 -1
- package/dist/cjs/index.d.cts +1 -1
- package/dist/cjs/load-matches.cjs +4 -4
- package/dist/cjs/load-matches.cjs.map +1 -1
- package/dist/cjs/router.cjs +44 -45
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/router.d.cts +3 -1
- package/dist/cjs/scroll-restoration.cjs +1 -1
- package/dist/cjs/scroll-restoration.cjs.map +1 -1
- package/dist/cjs/ssr/createRequestHandler.cjs +2 -2
- package/dist/cjs/ssr/createRequestHandler.cjs.map +1 -1
- package/dist/cjs/ssr/serializer/RawStream.cjs +41 -32
- package/dist/cjs/ssr/serializer/RawStream.cjs.map +1 -1
- package/dist/cjs/ssr/serializer/RawStream.d.cts +12 -4
- package/dist/cjs/ssr/serializer/ShallowErrorPlugin.cjs.map +1 -1
- package/dist/cjs/ssr/serializer/ShallowErrorPlugin.d.cts +2 -2
- package/dist/cjs/ssr/serializer/seroval-plugins.cjs.map +1 -1
- package/dist/cjs/ssr/serializer/seroval-plugins.d.cts +2 -1
- package/dist/cjs/ssr/serializer/transformer.cjs +16 -14
- package/dist/cjs/ssr/serializer/transformer.cjs.map +1 -1
- package/dist/cjs/ssr/serializer/transformer.d.cts +24 -23
- package/dist/cjs/ssr/ssr-client.cjs +8 -8
- package/dist/cjs/ssr/ssr-client.cjs.map +1 -1
- package/dist/cjs/ssr/ssr-server.cjs +31 -9
- package/dist/cjs/ssr/ssr-server.cjs.map +1 -1
- package/dist/cjs/ssr/ssr-server.d.cts +3 -2
- package/dist/cjs/ssr/transformStreamWithRouter.cjs +4 -1
- package/dist/cjs/ssr/transformStreamWithRouter.cjs.map +1 -1
- package/dist/cjs/stores.cjs +27 -27
- package/dist/cjs/stores.cjs.map +1 -1
- package/dist/cjs/stores.d.cts +2 -2
- package/dist/esm/hash-scroll.js +1 -1
- package/dist/esm/hash-scroll.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/load-matches.js +4 -4
- package/dist/esm/load-matches.js.map +1 -1
- package/dist/esm/router.d.ts +3 -1
- package/dist/esm/router.js +44 -45
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/scroll-restoration.js +1 -1
- package/dist/esm/scroll-restoration.js.map +1 -1
- package/dist/esm/ssr/createRequestHandler.js +2 -2
- package/dist/esm/ssr/createRequestHandler.js.map +1 -1
- package/dist/esm/ssr/serializer/RawStream.d.ts +12 -4
- package/dist/esm/ssr/serializer/RawStream.js +41 -32
- package/dist/esm/ssr/serializer/RawStream.js.map +1 -1
- package/dist/esm/ssr/serializer/ShallowErrorPlugin.d.ts +2 -2
- package/dist/esm/ssr/serializer/ShallowErrorPlugin.js.map +1 -1
- package/dist/esm/ssr/serializer/seroval-plugins.d.ts +2 -1
- package/dist/esm/ssr/serializer/seroval-plugins.js.map +1 -1
- package/dist/esm/ssr/serializer/transformer.d.ts +24 -23
- package/dist/esm/ssr/serializer/transformer.js +16 -14
- package/dist/esm/ssr/serializer/transformer.js.map +1 -1
- package/dist/esm/ssr/ssr-client.js +8 -8
- package/dist/esm/ssr/ssr-client.js.map +1 -1
- package/dist/esm/ssr/ssr-server.d.ts +3 -2
- package/dist/esm/ssr/ssr-server.js +31 -9
- package/dist/esm/ssr/ssr-server.js.map +1 -1
- package/dist/esm/ssr/transformStreamWithRouter.js +4 -1
- package/dist/esm/ssr/transformStreamWithRouter.js.map +1 -1
- package/dist/esm/stores.d.ts +2 -2
- package/dist/esm/stores.js +28 -28
- package/dist/esm/stores.js.map +1 -1
- package/package.json +3 -3
- package/src/hash-scroll.ts +1 -1
- package/src/index.ts +1 -1
- package/src/load-matches.ts +6 -6
- package/src/router.ts +59 -57
- package/src/scroll-restoration.ts +1 -1
- package/src/ssr/createRequestHandler.ts +4 -5
- package/src/ssr/serializer/RawStream.ts +65 -56
- package/src/ssr/serializer/ShallowErrorPlugin.ts +2 -2
- package/src/ssr/serializer/seroval-plugins.ts +2 -1
- package/src/ssr/serializer/transformer.ts +71 -76
- package/src/ssr/ssr-client.ts +7 -11
- package/src/ssr/ssr-server.ts +39 -7
- package/src/ssr/transformStreamWithRouter.ts +3 -0
- package/src/stores.ts +34 -34
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createPlugin, createStream } from 'seroval'
|
|
2
|
-
import type {
|
|
2
|
+
import type { PluginData, PluginInfo, SerovalNode } from 'seroval'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Hint for RawStream encoding strategy during SSR serialization.
|
|
@@ -240,61 +240,71 @@ function toTextStream(readable: ReadableStream<Uint8Array>) {
|
|
|
240
240
|
}
|
|
241
241
|
|
|
242
242
|
// Factory plugin for binary mode
|
|
243
|
-
const RawStreamFactoryBinaryPlugin = createPlugin<
|
|
243
|
+
const RawStreamFactoryBinaryPlugin = /* @__PURE__ */ createPlugin<
|
|
244
244
|
Record<string, never>,
|
|
245
|
-
|
|
245
|
+
PluginInfo
|
|
246
246
|
>({
|
|
247
247
|
tag: 'tss/RawStreamFactory',
|
|
248
248
|
test(value) {
|
|
249
249
|
return value === RAW_STREAM_FACTORY_BINARY
|
|
250
250
|
},
|
|
251
251
|
parse: {
|
|
252
|
-
sync() {
|
|
253
|
-
return
|
|
252
|
+
sync(_value, _ctx, _data) {
|
|
253
|
+
return {}
|
|
254
254
|
},
|
|
255
|
-
async() {
|
|
256
|
-
return
|
|
255
|
+
async async(_value, _ctx, _data) {
|
|
256
|
+
return {}
|
|
257
257
|
},
|
|
258
|
-
stream() {
|
|
259
|
-
return
|
|
258
|
+
stream(_value, _ctx, _data) {
|
|
259
|
+
return {}
|
|
260
260
|
},
|
|
261
261
|
},
|
|
262
|
-
serialize() {
|
|
262
|
+
serialize(_node, _ctx, _data) {
|
|
263
263
|
return FACTORY_BINARY
|
|
264
264
|
},
|
|
265
|
-
deserialize() {
|
|
265
|
+
deserialize(_node, _ctx, _data) {
|
|
266
266
|
return RAW_STREAM_FACTORY_BINARY
|
|
267
267
|
},
|
|
268
268
|
})
|
|
269
269
|
|
|
270
270
|
// Factory plugin for text mode
|
|
271
|
-
const RawStreamFactoryTextPlugin = createPlugin<
|
|
271
|
+
const RawStreamFactoryTextPlugin = /* @__PURE__ */ createPlugin<
|
|
272
272
|
Record<string, never>,
|
|
273
|
-
|
|
273
|
+
PluginInfo
|
|
274
274
|
>({
|
|
275
275
|
tag: 'tss/RawStreamFactoryText',
|
|
276
276
|
test(value) {
|
|
277
277
|
return value === RAW_STREAM_FACTORY_TEXT
|
|
278
278
|
},
|
|
279
279
|
parse: {
|
|
280
|
-
sync() {
|
|
281
|
-
return
|
|
280
|
+
sync(_value, _ctx, _data) {
|
|
281
|
+
return {}
|
|
282
282
|
},
|
|
283
|
-
async() {
|
|
284
|
-
return
|
|
283
|
+
async async(_value, _ctx, _data) {
|
|
284
|
+
return {}
|
|
285
285
|
},
|
|
286
|
-
stream() {
|
|
287
|
-
return
|
|
286
|
+
stream(_value, _ctx, _data) {
|
|
287
|
+
return {}
|
|
288
288
|
},
|
|
289
289
|
},
|
|
290
|
-
serialize() {
|
|
290
|
+
serialize(_node, _ctx, _data) {
|
|
291
291
|
return FACTORY_TEXT
|
|
292
292
|
},
|
|
293
|
-
deserialize() {
|
|
293
|
+
deserialize(_node, _ctx, _data) {
|
|
294
294
|
return RAW_STREAM_FACTORY_TEXT
|
|
295
295
|
},
|
|
296
296
|
})
|
|
297
297
|
|
|
298
|
+
export interface RawStreamSSRNode extends PluginInfo {
|
|
299
|
+
hint: SerovalNode
|
|
300
|
+
factory: SerovalNode
|
|
301
|
+
stream: SerovalNode
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export interface RawStreamRPCNode extends PluginInfo {
|
|
305
|
+
streamId: SerovalNode
|
|
306
|
+
}
|
|
307
|
+
|
|
298
308
|
/**
|
|
299
309
|
* SSR Plugin - uses base64 or UTF-8+base64 encoding for chunks, delegates to seroval's stream mechanism.
|
|
300
310
|
* Used during SSR when serializing to JavaScript code for HTML injection.
|
|
@@ -303,7 +313,10 @@ const RawStreamFactoryTextPlugin = createPlugin<
|
|
|
303
313
|
* - 'binary': Always base64 encode (default)
|
|
304
314
|
* - 'text': Try UTF-8 first, fallback to base64 for invalid UTF-8
|
|
305
315
|
*/
|
|
306
|
-
export const RawStreamSSRPlugin
|
|
316
|
+
export const RawStreamSSRPlugin = /* @__PURE__ */ createPlugin<
|
|
317
|
+
RawStream,
|
|
318
|
+
RawStreamSSRNode
|
|
319
|
+
>({
|
|
307
320
|
tag: 'tss/RawStream',
|
|
308
321
|
extends: [RawStreamFactoryBinaryPlugin, RawStreamFactoryTextPlugin],
|
|
309
322
|
|
|
@@ -312,19 +325,19 @@ export const RawStreamSSRPlugin: Plugin<any, any> = createPlugin({
|
|
|
312
325
|
},
|
|
313
326
|
|
|
314
327
|
parse: {
|
|
315
|
-
sync(value: RawStream, ctx) {
|
|
328
|
+
sync(value: RawStream, ctx, _data) {
|
|
316
329
|
// Sync parse not really supported for streams, return empty stream
|
|
317
330
|
const factory =
|
|
318
331
|
value.hint === 'text'
|
|
319
332
|
? RAW_STREAM_FACTORY_TEXT
|
|
320
333
|
: RAW_STREAM_FACTORY_BINARY
|
|
321
334
|
return {
|
|
322
|
-
hint: value.hint,
|
|
335
|
+
hint: ctx.parse(value.hint),
|
|
323
336
|
factory: ctx.parse(factory),
|
|
324
337
|
stream: ctx.parse(createStream()),
|
|
325
338
|
}
|
|
326
339
|
},
|
|
327
|
-
async async(value: RawStream, ctx) {
|
|
340
|
+
async async(value: RawStream, ctx, _data) {
|
|
328
341
|
const factory =
|
|
329
342
|
value.hint === 'text'
|
|
330
343
|
? RAW_STREAM_FACTORY_TEXT
|
|
@@ -334,12 +347,12 @@ export const RawStreamSSRPlugin: Plugin<any, any> = createPlugin({
|
|
|
334
347
|
? toTextStream(value.stream)
|
|
335
348
|
: toBinaryStream(value.stream)
|
|
336
349
|
return {
|
|
337
|
-
hint: value.hint,
|
|
350
|
+
hint: await ctx.parse(value.hint),
|
|
338
351
|
factory: await ctx.parse(factory),
|
|
339
352
|
stream: await ctx.parse(encodedStream),
|
|
340
353
|
}
|
|
341
354
|
},
|
|
342
|
-
stream(value: RawStream, ctx) {
|
|
355
|
+
stream(value: RawStream, ctx, _data) {
|
|
343
356
|
const factory =
|
|
344
357
|
value.hint === 'text'
|
|
345
358
|
? RAW_STREAM_FACTORY_TEXT
|
|
@@ -349,14 +362,14 @@ export const RawStreamSSRPlugin: Plugin<any, any> = createPlugin({
|
|
|
349
362
|
? toTextStream(value.stream)
|
|
350
363
|
: toBinaryStream(value.stream)
|
|
351
364
|
return {
|
|
352
|
-
hint: value.hint,
|
|
365
|
+
hint: ctx.parse(value.hint),
|
|
353
366
|
factory: ctx.parse(factory),
|
|
354
367
|
stream: ctx.parse(encodedStream),
|
|
355
368
|
}
|
|
356
369
|
},
|
|
357
370
|
},
|
|
358
371
|
|
|
359
|
-
serialize(node:
|
|
372
|
+
serialize(node: RawStreamSSRNode, ctx, _data) {
|
|
360
373
|
return (
|
|
361
374
|
'(' +
|
|
362
375
|
ctx.serialize(node.factory) +
|
|
@@ -366,23 +379,14 @@ export const RawStreamSSRPlugin: Plugin<any, any> = createPlugin({
|
|
|
366
379
|
)
|
|
367
380
|
},
|
|
368
381
|
|
|
369
|
-
deserialize(
|
|
370
|
-
node: { hint: RawStreamHint; factory: any; stream: any },
|
|
371
|
-
ctx,
|
|
372
|
-
): any {
|
|
382
|
+
deserialize(node: RawStreamSSRNode, ctx, _data): any {
|
|
373
383
|
const stream: ReturnType<typeof createStream> = ctx.deserialize(node.stream)
|
|
374
|
-
|
|
384
|
+
const hint = ctx.deserialize(node.hint)
|
|
385
|
+
return hint === 'text'
|
|
375
386
|
? RAW_STREAM_FACTORY_CONSTRUCTOR_TEXT(stream)
|
|
376
387
|
: RAW_STREAM_FACTORY_CONSTRUCTOR_BINARY(stream)
|
|
377
388
|
},
|
|
378
|
-
})
|
|
379
|
-
|
|
380
|
-
/**
|
|
381
|
-
* Node type for RPC plugin serialization
|
|
382
|
-
*/
|
|
383
|
-
interface RawStreamRPCNode {
|
|
384
|
-
streamId: number
|
|
385
|
-
}
|
|
389
|
+
})
|
|
386
390
|
|
|
387
391
|
/**
|
|
388
392
|
* Creates an RPC plugin instance that registers raw streams with a multiplexer.
|
|
@@ -391,13 +395,12 @@ interface RawStreamRPCNode {
|
|
|
391
395
|
*
|
|
392
396
|
* @param onRawStream Callback invoked when a RawStream is encountered during serialization
|
|
393
397
|
*/
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
): Plugin<any, any> {
|
|
398
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
399
|
+
export function createRawStreamRPCPlugin(onRawStream: OnRawStreamCallback) {
|
|
397
400
|
// Own stream counter - sequential IDs starting at 1, independent of seroval internals
|
|
398
401
|
let nextStreamId = 1
|
|
399
402
|
|
|
400
|
-
return createPlugin({
|
|
403
|
+
return /* @__PURE__ */ createPlugin<RawStream, RawStreamRPCNode>({
|
|
401
404
|
tag: 'tss/RawStream',
|
|
402
405
|
|
|
403
406
|
test(value: unknown) {
|
|
@@ -405,15 +408,15 @@ export function createRawStreamRPCPlugin(
|
|
|
405
408
|
},
|
|
406
409
|
|
|
407
410
|
parse: {
|
|
408
|
-
async(value: RawStream) {
|
|
411
|
+
async async(value: RawStream, ctx, _data: PluginData) {
|
|
409
412
|
const streamId = nextStreamId++
|
|
410
413
|
onRawStream(streamId, value.stream)
|
|
411
|
-
return
|
|
414
|
+
return { streamId: await ctx.parse(streamId) }
|
|
412
415
|
},
|
|
413
|
-
stream(value: RawStream) {
|
|
416
|
+
stream(value: RawStream, ctx, _data: PluginData) {
|
|
414
417
|
const streamId = nextStreamId++
|
|
415
418
|
onRawStream(streamId, value.stream)
|
|
416
|
-
return { streamId }
|
|
419
|
+
return { streamId: ctx.parse(streamId) }
|
|
417
420
|
},
|
|
418
421
|
},
|
|
419
422
|
|
|
@@ -431,7 +434,7 @@ export function createRawStreamRPCPlugin(
|
|
|
431
434
|
'RawStreamRPCPlugin.deserialize should not be called. Use createRawStreamDeserializePlugin on client.',
|
|
432
435
|
)
|
|
433
436
|
},
|
|
434
|
-
})
|
|
437
|
+
})
|
|
435
438
|
}
|
|
436
439
|
|
|
437
440
|
/**
|
|
@@ -442,8 +445,8 @@ export function createRawStreamRPCPlugin(
|
|
|
442
445
|
*/
|
|
443
446
|
export function createRawStreamDeserializePlugin(
|
|
444
447
|
getOrCreateStream: (id: number) => ReadableStream<Uint8Array>,
|
|
445
|
-
)
|
|
446
|
-
return createPlugin({
|
|
448
|
+
) {
|
|
449
|
+
return /* @__PURE__ */ createPlugin<any, RawStreamRPCNode>({
|
|
447
450
|
tag: 'tss/RawStream',
|
|
448
451
|
|
|
449
452
|
test: () => false, // Client never serializes RawStream
|
|
@@ -457,8 +460,14 @@ export function createRawStreamDeserializePlugin(
|
|
|
457
460
|
)
|
|
458
461
|
},
|
|
459
462
|
|
|
460
|
-
deserialize(node
|
|
461
|
-
|
|
463
|
+
deserialize(node, ctx, _data) {
|
|
464
|
+
// In normal seroval usage, ctx.deserialize exists.
|
|
465
|
+
// Some unit tests call plugin.deserialize directly with a minimal ctx.
|
|
466
|
+
const id =
|
|
467
|
+
typeof (ctx as any)?.deserialize === 'function'
|
|
468
|
+
? (ctx as any).deserialize(node.streamId)
|
|
469
|
+
: (node as any).streamId
|
|
470
|
+
return getOrCreateStream(id as number)
|
|
462
471
|
},
|
|
463
|
-
})
|
|
472
|
+
})
|
|
464
473
|
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { ReadableStreamPlugin } from 'seroval-plugins/web'
|
|
2
2
|
import { ShallowErrorPlugin } from './ShallowErrorPlugin'
|
|
3
3
|
import { RawStreamSSRPlugin } from './RawStream'
|
|
4
|
+
import type { RawStream } from './RawStream'
|
|
4
5
|
import type { Plugin } from 'seroval'
|
|
5
6
|
|
|
6
7
|
export const defaultSerovalPlugins = [
|
|
7
8
|
ShallowErrorPlugin as Plugin<Error, any>,
|
|
8
9
|
// RawStreamSSRPlugin must come before ReadableStreamPlugin to match first
|
|
9
|
-
RawStreamSSRPlugin,
|
|
10
|
+
RawStreamSSRPlugin as Plugin<RawStream, any>,
|
|
10
11
|
// ReadableStreamNode is not exported by seroval
|
|
11
12
|
ReadableStreamPlugin as Plugin<ReadableStream, any>,
|
|
12
13
|
]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createPlugin } from 'seroval'
|
|
2
2
|
import { GLOBAL_TSR } from '../constants'
|
|
3
|
-
import type { Plugin, SerovalNode } from 'seroval'
|
|
3
|
+
import type { Plugin, PluginInfo, SerovalNode } from 'seroval'
|
|
4
4
|
import type {
|
|
5
5
|
RegisteredConfigType,
|
|
6
6
|
RegisteredSsr,
|
|
@@ -14,6 +14,7 @@ declare const TSR_SERIALIZABLE: unique symbol
|
|
|
14
14
|
export type TSR_SERIALIZABLE = typeof TSR_SERIALIZABLE
|
|
15
15
|
|
|
16
16
|
export type TsrSerializable = { [TSR_SERIALIZABLE]: true }
|
|
17
|
+
|
|
17
18
|
export interface DefaultSerializable {
|
|
18
19
|
number: number
|
|
19
20
|
string: string
|
|
@@ -25,6 +26,7 @@ export interface DefaultSerializable {
|
|
|
25
26
|
Uint8Array: Uint8Array
|
|
26
27
|
RawStream: RawStream
|
|
27
28
|
TsrSerializable: TsrSerializable
|
|
29
|
+
void: void
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
export interface SerializableExtensions extends DefaultSerializable {}
|
|
@@ -72,13 +74,14 @@ export interface CreateSerializationAdapterOptions<
|
|
|
72
74
|
fromSerializable: (value: TOutput) => TInput
|
|
73
75
|
}
|
|
74
76
|
|
|
75
|
-
export type ValidateSerializable<T, TSerializable> =
|
|
76
|
-
T
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
77
|
+
export type ValidateSerializable<T, TSerializable> = T extends TSerializable
|
|
78
|
+
? T
|
|
79
|
+
: T extends (...args: Array<any>) => any
|
|
80
|
+
? SerializationError<'Function may not be serializable'>
|
|
81
|
+
: T extends RegisteredReadableStream
|
|
82
|
+
? SerializationError<'JSX is not be serializable'>
|
|
83
|
+
: T extends ReadonlyArray<any>
|
|
84
|
+
? ValidateSerializableArray<T, TSerializable>
|
|
82
85
|
: T extends Promise<any>
|
|
83
86
|
? ValidateSerializablePromise<T, TSerializable>
|
|
84
87
|
: T extends ReadableStream<any>
|
|
@@ -89,9 +92,9 @@ export type ValidateSerializable<T, TSerializable> =
|
|
|
89
92
|
? ValidateSerializableMap<T, TSerializable>
|
|
90
93
|
: T extends AsyncGenerator<any, any>
|
|
91
94
|
? ValidateSerializableAsyncGenerator<T, TSerializable>
|
|
92
|
-
:
|
|
93
|
-
|
|
94
|
-
|
|
95
|
+
: T extends object
|
|
96
|
+
? ValidateSerializableMapped<T, TSerializable>
|
|
97
|
+
: SerializationError<'Type may not be serializable'>
|
|
95
98
|
|
|
96
99
|
export type ValidateSerializableAsyncGenerator<T, TSerializable> =
|
|
97
100
|
T extends AsyncGenerator<infer T, infer TReturn, infer TNext>
|
|
@@ -125,16 +128,26 @@ export type ValidateSerializableMap<T, TSerializable> =
|
|
|
125
128
|
>
|
|
126
129
|
: never
|
|
127
130
|
|
|
128
|
-
export type
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
131
|
+
export type ValidateSerializableArray<T, TSerializable> = T extends readonly [
|
|
132
|
+
any,
|
|
133
|
+
...Array<any>,
|
|
134
|
+
]
|
|
135
|
+
? ValidateSerializableMapped<T, TSerializable>
|
|
136
|
+
: T extends Array<infer U>
|
|
137
|
+
? Array<ValidateSerializable<U, TSerializable>>
|
|
138
|
+
: T extends ReadonlyArray<infer U>
|
|
139
|
+
? ReadonlyArray<ValidateSerializable<U, TSerializable>>
|
|
140
|
+
: never
|
|
132
141
|
|
|
133
|
-
export
|
|
134
|
-
|
|
142
|
+
export type ValidateSerializableMapped<T, TSerializable> = {
|
|
143
|
+
[K in keyof T]: ValidateSerializable<T[K], TSerializable>
|
|
135
144
|
}
|
|
136
145
|
|
|
137
|
-
|
|
146
|
+
const SERIALIZATION_ERROR = Symbol.for('TSR_SERIALIZATION_ERROR')
|
|
147
|
+
|
|
148
|
+
export interface SerializationError<in out TMessage extends string> {
|
|
149
|
+
[SERIALIZATION_ERROR]: TMessage
|
|
150
|
+
}
|
|
138
151
|
|
|
139
152
|
export interface SerializationAdapter<
|
|
140
153
|
TInput,
|
|
@@ -161,27 +174,34 @@ export interface SerializationAdapterTypes<
|
|
|
161
174
|
|
|
162
175
|
export type AnySerializationAdapter = SerializationAdapter<any, any, any>
|
|
163
176
|
|
|
177
|
+
export interface AdapterNode extends PluginInfo {
|
|
178
|
+
v: SerovalNode
|
|
179
|
+
}
|
|
180
|
+
|
|
164
181
|
/** Create a Seroval plugin for server-side serialization only. */
|
|
182
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
165
183
|
export function makeSsrSerovalPlugin(
|
|
166
184
|
serializationAdapter: AnySerializationAdapter,
|
|
167
185
|
options: { didRun: boolean },
|
|
168
|
-
): Plugin<any,
|
|
169
|
-
return createPlugin<any,
|
|
186
|
+
): Plugin<any, AdapterNode> {
|
|
187
|
+
return /* @__PURE__ */ createPlugin<any, AdapterNode>({
|
|
170
188
|
tag: '$TSR/t/' + serializationAdapter.key,
|
|
171
189
|
test: serializationAdapter.test,
|
|
172
190
|
parse: {
|
|
173
|
-
stream(value, ctx) {
|
|
174
|
-
return
|
|
191
|
+
stream(value, ctx, _data) {
|
|
192
|
+
return {
|
|
193
|
+
v: ctx.parse(serializationAdapter.toSerializable(value)),
|
|
194
|
+
}
|
|
175
195
|
},
|
|
176
196
|
},
|
|
177
|
-
serialize(node, ctx) {
|
|
197
|
+
serialize(node, ctx, _data) {
|
|
178
198
|
options.didRun = true
|
|
179
199
|
return (
|
|
180
200
|
GLOBAL_TSR +
|
|
181
201
|
'.t.get("' +
|
|
182
202
|
serializationAdapter.key +
|
|
183
203
|
'")(' +
|
|
184
|
-
ctx.serialize(node) +
|
|
204
|
+
ctx.serialize(node.v) +
|
|
185
205
|
')'
|
|
186
206
|
)
|
|
187
207
|
},
|
|
@@ -191,27 +211,34 @@ export function makeSsrSerovalPlugin(
|
|
|
191
211
|
}
|
|
192
212
|
|
|
193
213
|
/** Create a Seroval plugin for client/server symmetric (de)serialization. */
|
|
214
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
194
215
|
export function makeSerovalPlugin(
|
|
195
216
|
serializationAdapter: AnySerializationAdapter,
|
|
196
|
-
): Plugin<any,
|
|
197
|
-
return createPlugin<any,
|
|
217
|
+
): Plugin<any, AdapterNode> {
|
|
218
|
+
return /* @__PURE__ */ createPlugin<any, AdapterNode>({
|
|
198
219
|
tag: '$TSR/t/' + serializationAdapter.key,
|
|
199
220
|
test: serializationAdapter.test,
|
|
200
221
|
parse: {
|
|
201
|
-
sync(value, ctx) {
|
|
202
|
-
return
|
|
222
|
+
sync(value, ctx, _data) {
|
|
223
|
+
return {
|
|
224
|
+
v: ctx.parse(serializationAdapter.toSerializable(value)),
|
|
225
|
+
}
|
|
203
226
|
},
|
|
204
|
-
async async(value, ctx) {
|
|
205
|
-
return
|
|
227
|
+
async async(value, ctx, _data) {
|
|
228
|
+
return {
|
|
229
|
+
v: await ctx.parse(serializationAdapter.toSerializable(value)),
|
|
230
|
+
}
|
|
206
231
|
},
|
|
207
|
-
stream(value, ctx) {
|
|
208
|
-
return
|
|
232
|
+
stream(value, ctx, _data) {
|
|
233
|
+
return {
|
|
234
|
+
v: ctx.parse(serializationAdapter.toSerializable(value)),
|
|
235
|
+
}
|
|
209
236
|
},
|
|
210
237
|
},
|
|
211
238
|
// we don't generate JS code outside of SSR (for now)
|
|
212
239
|
serialize: undefined as never,
|
|
213
|
-
deserialize(node, ctx) {
|
|
214
|
-
return serializationAdapter.fromSerializable(ctx.deserialize(node))
|
|
240
|
+
deserialize(node, ctx, _data) {
|
|
241
|
+
return serializationAdapter.fromSerializable(ctx.deserialize(node.v))
|
|
215
242
|
},
|
|
216
243
|
})
|
|
217
244
|
}
|
|
@@ -234,20 +261,6 @@ export type RegisteredSerializationAdapters<TRegister> = RegisteredConfigType<
|
|
|
234
261
|
'serializationAdapters'
|
|
235
262
|
>
|
|
236
263
|
|
|
237
|
-
export type ValidateSerializableInputResult<TRegister, T> =
|
|
238
|
-
ValidateSerializableResult<T, RegisteredSerializableInput<TRegister>>
|
|
239
|
-
|
|
240
|
-
export type ValidateSerializableResult<T, TSerializable> =
|
|
241
|
-
T extends ReadonlyArray<unknown>
|
|
242
|
-
? ResolveArrayShape<T, TSerializable, 'result'>
|
|
243
|
-
: T extends TSerializable
|
|
244
|
-
? T
|
|
245
|
-
: unknown extends SerializerExtensions['ReadableStream']
|
|
246
|
-
? { [K in keyof T]: ValidateSerializableResult<T[K], TSerializable> }
|
|
247
|
-
: T extends SerializerExtensions['ReadableStream']
|
|
248
|
-
? ReadableStream
|
|
249
|
-
: { [K in keyof T]: ValidateSerializableResult<T[K], TSerializable> }
|
|
250
|
-
|
|
251
264
|
export type RegisteredSSROption<TRegister> =
|
|
252
265
|
unknown extends RegisteredConfigType<TRegister, 'defaultSsr'>
|
|
253
266
|
? SSROption
|
|
@@ -282,31 +295,13 @@ export type ValidateSerializableLifecycleResultSSR<
|
|
|
282
295
|
? any
|
|
283
296
|
: ValidateSerializableInput<TRegister, LooseReturnType<TFn>>
|
|
284
297
|
|
|
285
|
-
type
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
type ResolveTupleShape<
|
|
296
|
-
T extends ReadonlyArray<unknown>,
|
|
297
|
-
TSerializable,
|
|
298
|
-
TMode extends 'input' | 'result',
|
|
299
|
-
> = T extends readonly [infer THead, ...infer TTail]
|
|
300
|
-
? readonly [
|
|
301
|
-
ArrayModeResult<TMode, THead, TSerializable>,
|
|
302
|
-
...ResolveTupleShape<Readonly<TTail>, TSerializable, TMode>,
|
|
303
|
-
]
|
|
304
|
-
: T
|
|
305
|
-
|
|
306
|
-
type ArrayModeResult<
|
|
307
|
-
TMode extends 'input' | 'result',
|
|
308
|
-
TValue,
|
|
309
|
-
TSerializable,
|
|
310
|
-
> = TMode extends 'input'
|
|
311
|
-
? ValidateSerializable<TValue, TSerializable>
|
|
312
|
-
: ValidateSerializableResult<TValue, TSerializable>
|
|
298
|
+
export type RegisteredReadableStream =
|
|
299
|
+
unknown extends SerializerExtensions['ReadableStream']
|
|
300
|
+
? never
|
|
301
|
+
: SerializerExtensions['ReadableStream']
|
|
302
|
+
|
|
303
|
+
export interface DefaultSerializerExtensions {
|
|
304
|
+
ReadableStream: unknown
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
export interface SerializerExtensions extends DefaultSerializerExtensions {}
|
package/src/ssr/ssr-client.ts
CHANGED
|
@@ -95,7 +95,7 @@ export async function hydrate(router: AnyRouter): Promise<any> {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
// Hydrate the router state
|
|
98
|
-
const matches = router.matchRoutes(router.stores.location.
|
|
98
|
+
const matches = router.matchRoutes(router.stores.location.get())
|
|
99
99
|
|
|
100
100
|
// kick off loading the route chunks
|
|
101
101
|
const routeChunkPromise = Promise.all(
|
|
@@ -170,8 +170,8 @@ export async function hydrate(router: AnyRouter): Promise<any> {
|
|
|
170
170
|
// now that all necessary data is hydrated:
|
|
171
171
|
// 1) fully reconstruct the route context
|
|
172
172
|
// 2) execute `head()` and `scripts()` for each match
|
|
173
|
-
const activeMatches = router.stores.activeMatchesSnapshot.
|
|
174
|
-
const location = router.stores.location.
|
|
173
|
+
const activeMatches = router.stores.activeMatchesSnapshot.get()
|
|
174
|
+
const location = router.stores.location.get()
|
|
175
175
|
await Promise.all(
|
|
176
176
|
activeMatches.map(async (match) => {
|
|
177
177
|
try {
|
|
@@ -258,7 +258,7 @@ export async function hydrate(router: AnyRouter): Promise<any> {
|
|
|
258
258
|
// (e.g. preloads, invalidations) don't mistakenly detect a href change
|
|
259
259
|
// (resolvedLocation defaults to undefined and router.load() is skipped
|
|
260
260
|
// in the normal SSR hydration path).
|
|
261
|
-
router.stores.resolvedLocation.
|
|
261
|
+
router.stores.resolvedLocation.set(router.stores.location.get())
|
|
262
262
|
return routeChunkPromise
|
|
263
263
|
}
|
|
264
264
|
|
|
@@ -292,13 +292,9 @@ export async function hydrate(router: AnyRouter): Promise<any> {
|
|
|
292
292
|
// ensure router is not in status 'pending' anymore
|
|
293
293
|
// this usually happens in Transitioner but if loading synchronously resolves,
|
|
294
294
|
// Transitioner won't be rendered while loading so it cannot track the change from loading:true to loading:false
|
|
295
|
-
if (router.stores.status.
|
|
296
|
-
router.
|
|
297
|
-
|
|
298
|
-
router.stores.resolvedLocation.setState(
|
|
299
|
-
() => router.stores.location.state,
|
|
300
|
-
)
|
|
301
|
-
})
|
|
295
|
+
if (router.stores.status.get() === 'pending') {
|
|
296
|
+
router.stores.status.set('idle')
|
|
297
|
+
router.stores.resolvedLocation.set(router.stores.location.get())
|
|
302
298
|
}
|
|
303
299
|
// hide the pending component once the load is finished
|
|
304
300
|
router.updateMatch(match.id, (prev) => ({
|
package/src/ssr/ssr-server.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { crossSerializeStream, getCrossReferenceHeader } from 'seroval'
|
|
|
2
2
|
import { invariant } from '../invariant'
|
|
3
3
|
import { decodePath } from '../utils'
|
|
4
4
|
import { createLRUCache } from '../lru-cache'
|
|
5
|
+
import { rootRouteId } from '../root'
|
|
5
6
|
import minifiedTsrBootStrapScript from './tsrScript?script-string'
|
|
6
7
|
import { GLOBAL_TSR, TSR_SCRIPT_BARRIER_ID } from './constants'
|
|
7
8
|
import { dehydrateSsrMatchId } from './ssr-match-id'
|
|
@@ -171,12 +172,31 @@ function getManifestCache(manifest: Manifest): ManifestLRU {
|
|
|
171
172
|
export function attachRouterServerSsrUtils({
|
|
172
173
|
router,
|
|
173
174
|
manifest,
|
|
175
|
+
getRequestAssets,
|
|
174
176
|
}: {
|
|
175
177
|
router: AnyRouter
|
|
176
178
|
manifest: Manifest | undefined
|
|
179
|
+
getRequestAssets?: () => Array<RouterManagedTag> | undefined
|
|
177
180
|
}) {
|
|
178
181
|
router.ssr = {
|
|
179
|
-
manifest
|
|
182
|
+
get manifest() {
|
|
183
|
+
const requestAssets = getRequestAssets?.()
|
|
184
|
+
if (!requestAssets?.length) return manifest
|
|
185
|
+
// Merge request-scoped assets into root route without mutating cached manifest
|
|
186
|
+
return {
|
|
187
|
+
...manifest,
|
|
188
|
+
routes: {
|
|
189
|
+
...manifest?.routes,
|
|
190
|
+
[rootRouteId]: {
|
|
191
|
+
...manifest?.routes?.[rootRouteId],
|
|
192
|
+
assets: [
|
|
193
|
+
...requestAssets,
|
|
194
|
+
...(manifest?.routes?.[rootRouteId]?.assets ?? []),
|
|
195
|
+
],
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
}
|
|
199
|
+
},
|
|
180
200
|
}
|
|
181
201
|
let _dehydrated = false
|
|
182
202
|
let _serializationFinished = false
|
|
@@ -200,7 +220,7 @@ export function attachRouterServerSsrUtils({
|
|
|
200
220
|
const html = `<script${router.options.ssr?.nonce ? ` nonce='${router.options.ssr.nonce}'` : ''}>${script}</script>`
|
|
201
221
|
router.serverSsr!.injectHtml(html)
|
|
202
222
|
},
|
|
203
|
-
dehydrate: async () => {
|
|
223
|
+
dehydrate: async (opts?: { requestAssets?: Array<RouterManagedTag> }) => {
|
|
204
224
|
if (_dehydrated) {
|
|
205
225
|
if (process.env.NODE_ENV !== 'production') {
|
|
206
226
|
throw new Error('Invariant failed: router is already dehydrated!')
|
|
@@ -208,7 +228,7 @@ export function attachRouterServerSsrUtils({
|
|
|
208
228
|
|
|
209
229
|
invariant()
|
|
210
230
|
}
|
|
211
|
-
let matchesToDehydrate = router.stores.activeMatchesSnapshot.
|
|
231
|
+
let matchesToDehydrate = router.stores.activeMatchesSnapshot.get()
|
|
212
232
|
if (router.isShell()) {
|
|
213
233
|
// In SPA mode we only want to dehydrate the root match
|
|
214
234
|
matchesToDehydrate = matchesToDehydrate.slice(0, 1)
|
|
@@ -257,6 +277,15 @@ export function attachRouterServerSsrUtils({
|
|
|
257
277
|
manifestToDehydrate = {
|
|
258
278
|
routes: filteredRoutes,
|
|
259
279
|
}
|
|
280
|
+
|
|
281
|
+
// Merge request-scoped assets into root route (without mutating cached manifest)
|
|
282
|
+
if (opts?.requestAssets?.length) {
|
|
283
|
+
const existingRoot = manifestToDehydrate.routes[rootRouteId]
|
|
284
|
+
manifestToDehydrate.routes[rootRouteId] = {
|
|
285
|
+
...existingRoot,
|
|
286
|
+
assets: [...opts.requestAssets, ...(existingRoot?.assets ?? [])],
|
|
287
|
+
}
|
|
288
|
+
}
|
|
260
289
|
}
|
|
261
290
|
const dehydratedRouter: DehydratedRouter = {
|
|
262
291
|
manifest: manifestToDehydrate,
|
|
@@ -305,6 +334,13 @@ export function attachRouterServerSsrUtils({
|
|
|
305
334
|
}
|
|
306
335
|
scriptBuffer.enqueue(serialized)
|
|
307
336
|
},
|
|
337
|
+
onError: (err: unknown) => {
|
|
338
|
+
console.error('Serialization error:', err)
|
|
339
|
+
if (err && (err as any).stack) {
|
|
340
|
+
console.error((err as any).stack)
|
|
341
|
+
}
|
|
342
|
+
signalSerializationComplete()
|
|
343
|
+
},
|
|
308
344
|
scopeId: SCOPE_ID,
|
|
309
345
|
onDone: () => {
|
|
310
346
|
scriptBuffer.enqueue(GLOBAL_TSR + '.e()')
|
|
@@ -313,10 +349,6 @@ export function attachRouterServerSsrUtils({
|
|
|
313
349
|
scriptBuffer.flush()
|
|
314
350
|
signalSerializationComplete()
|
|
315
351
|
},
|
|
316
|
-
onError: (err) => {
|
|
317
|
-
console.error('Serialization error:', err)
|
|
318
|
-
signalSerializationComplete()
|
|
319
|
-
},
|
|
320
352
|
})
|
|
321
353
|
},
|
|
322
354
|
isDehydrated() {
|