@tanstack/router-core 1.132.0-alpha.4 → 1.132.0-alpha.8
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 +2 -2
- package/dist/cjs/config.cjs +10 -0
- package/dist/cjs/config.cjs.map +1 -0
- package/dist/cjs/config.d.cts +17 -0
- package/dist/cjs/fileRoute.d.cts +3 -2
- package/dist/cjs/index.cjs +2 -0
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +3 -2
- package/dist/cjs/load-matches.cjs.map +1 -1
- package/dist/cjs/route.cjs.map +1 -1
- package/dist/cjs/route.d.cts +42 -37
- package/dist/cjs/router.cjs +25 -26
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/router.d.cts +19 -13
- package/dist/cjs/ssr/serializer/transformer.cjs +14 -12
- package/dist/cjs/ssr/serializer/transformer.cjs.map +1 -1
- package/dist/cjs/ssr/serializer/transformer.d.cts +53 -15
- package/dist/cjs/ssr/server.d.cts +5 -0
- package/dist/cjs/ssr/ssr-client.cjs.map +1 -1
- package/dist/cjs/ssr/ssr-server.cjs.map +1 -1
- package/dist/esm/Matches.d.ts +2 -2
- package/dist/esm/Matches.js.map +1 -1
- package/dist/esm/config.d.ts +17 -0
- package/dist/esm/config.js +10 -0
- package/dist/esm/config.js.map +1 -0
- package/dist/esm/fileRoute.d.ts +3 -2
- package/dist/esm/index.d.ts +3 -2
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/load-matches.js.map +1 -1
- package/dist/esm/route.d.ts +42 -37
- package/dist/esm/route.js.map +1 -1
- package/dist/esm/router.d.ts +19 -13
- package/dist/esm/router.js +25 -26
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/ssr/serializer/transformer.d.ts +53 -15
- package/dist/esm/ssr/serializer/transformer.js +14 -12
- package/dist/esm/ssr/serializer/transformer.js.map +1 -1
- package/dist/esm/ssr/server.d.ts +5 -0
- package/dist/esm/ssr/ssr-client.js.map +1 -1
- package/dist/esm/ssr/ssr-server.js.map +1 -1
- package/package.json +1 -1
- package/src/Matches.ts +2 -2
- package/src/config.ts +42 -0
- package/src/fileRoute.ts +15 -3
- package/src/index.ts +13 -1
- package/src/load-matches.ts +2 -2
- package/src/route.ts +136 -33
- package/src/router.ts +59 -64
- package/src/ssr/serializer/transformer.ts +168 -31
- package/src/ssr/server.ts +6 -0
- package/src/ssr/ssr-client.ts +2 -2
- package/src/ssr/ssr-server.ts +2 -2
package/src/router.ts
CHANGED
|
@@ -84,6 +84,8 @@ import type { Manifest } from './manifest'
|
|
|
84
84
|
import type { AnySchema, AnyValidator } from './validators'
|
|
85
85
|
import type { NavigateOptions, ResolveRelativePath, ToOptions } from './link'
|
|
86
86
|
import type { NotFoundError } from './not-found'
|
|
87
|
+
import type { AnySerializationAdapter } from './ssr/serializer/transformer'
|
|
88
|
+
import type { AnyRouterConfig } from './config'
|
|
87
89
|
|
|
88
90
|
export type ControllablePromise<T = any> = Promise<T> & {
|
|
89
91
|
resolve: (value: T) => void
|
|
@@ -94,6 +96,8 @@ export type InjectedHtmlEntry = Promise<string>
|
|
|
94
96
|
|
|
95
97
|
export interface DefaultRegister {
|
|
96
98
|
router: AnyRouter
|
|
99
|
+
config: AnyRouterConfig
|
|
100
|
+
ssr: SSROption
|
|
97
101
|
}
|
|
98
102
|
|
|
99
103
|
export interface Register extends DefaultRegister {
|
|
@@ -111,13 +115,14 @@ export interface DefaultRouterOptionsExtensions {}
|
|
|
111
115
|
export interface RouterOptionsExtensions
|
|
112
116
|
extends DefaultRouterOptionsExtensions {}
|
|
113
117
|
|
|
118
|
+
export type SSROption = boolean | 'data-only'
|
|
119
|
+
|
|
114
120
|
export interface RouterOptions<
|
|
115
121
|
TRouteTree extends AnyRoute,
|
|
116
122
|
TTrailingSlashOption extends TrailingSlashOption,
|
|
117
123
|
TDefaultStructuralSharingOption extends boolean = false,
|
|
118
124
|
TRouterHistory extends RouterHistory = RouterHistory,
|
|
119
125
|
TDehydrated extends Record<string, any> = Record<string, any>,
|
|
120
|
-
TTransformerConfig = any,
|
|
121
126
|
> extends RouterOptionsExtensions {
|
|
122
127
|
/**
|
|
123
128
|
* The history object that will be used to manage the browser history.
|
|
@@ -355,7 +360,7 @@ export interface RouterOptions<
|
|
|
355
360
|
*
|
|
356
361
|
* @default true
|
|
357
362
|
*/
|
|
358
|
-
defaultSsr?:
|
|
363
|
+
defaultSsr?: SSROption
|
|
359
364
|
|
|
360
365
|
search?: {
|
|
361
366
|
/**
|
|
@@ -425,7 +430,7 @@ export interface RouterOptions<
|
|
|
425
430
|
*/
|
|
426
431
|
disableGlobalCatchBoundary?: boolean
|
|
427
432
|
|
|
428
|
-
serializationAdapters?:
|
|
433
|
+
serializationAdapters?: ReadonlyArray<AnySerializationAdapter>
|
|
429
434
|
}
|
|
430
435
|
|
|
431
436
|
export interface RouterState<
|
|
@@ -534,15 +539,13 @@ export type RouterConstructorOptions<
|
|
|
534
539
|
TDefaultStructuralSharingOption extends boolean,
|
|
535
540
|
TRouterHistory extends RouterHistory,
|
|
536
541
|
TDehydrated extends Record<string, any>,
|
|
537
|
-
TTransformerConfig,
|
|
538
542
|
> = Omit<
|
|
539
543
|
RouterOptions<
|
|
540
544
|
TRouteTree,
|
|
541
545
|
TTrailingSlashOption,
|
|
542
546
|
TDefaultStructuralSharingOption,
|
|
543
547
|
TRouterHistory,
|
|
544
|
-
TDehydrated
|
|
545
|
-
TTransformerConfig
|
|
548
|
+
TDehydrated
|
|
546
549
|
>,
|
|
547
550
|
'context'
|
|
548
551
|
> &
|
|
@@ -602,15 +605,13 @@ export type UpdateFn<
|
|
|
602
605
|
TDefaultStructuralSharingOption extends boolean,
|
|
603
606
|
TRouterHistory extends RouterHistory,
|
|
604
607
|
TDehydrated extends Record<string, any>,
|
|
605
|
-
TTransformerConfig extends any,
|
|
606
608
|
> = (
|
|
607
609
|
newOptions: RouterConstructorOptions<
|
|
608
610
|
TRouteTree,
|
|
609
611
|
TTrailingSlashOption,
|
|
610
612
|
TDefaultStructuralSharingOption,
|
|
611
613
|
TRouterHistory,
|
|
612
|
-
TDehydrated
|
|
613
|
-
TTransformerConfig
|
|
614
|
+
TDehydrated
|
|
614
615
|
>,
|
|
615
616
|
) => void
|
|
616
617
|
|
|
@@ -621,8 +622,8 @@ export type InvalidateFn<TRouter extends AnyRouter> = (opts?: {
|
|
|
621
622
|
}) => Promise<void>
|
|
622
623
|
|
|
623
624
|
export type ParseLocationFn<TRouteTree extends AnyRoute> = (
|
|
625
|
+
locationToParse: HistoryLocation,
|
|
624
626
|
previousLocation?: ParsedLocation<FullSearchSchema<TRouteTree>>,
|
|
625
|
-
locationToParse?: HistoryLocation,
|
|
626
627
|
) => ParsedLocation<FullSearchSchema<TRouteTree>>
|
|
627
628
|
|
|
628
629
|
export type GetMatchRoutesFn = (
|
|
@@ -695,11 +696,10 @@ export type AnyRouterWithContext<TContext> = RouterCore<
|
|
|
695
696
|
any,
|
|
696
697
|
any,
|
|
697
698
|
any,
|
|
698
|
-
any,
|
|
699
699
|
any
|
|
700
700
|
>
|
|
701
701
|
|
|
702
|
-
export type AnyRouter = RouterCore<any, any, any, any, any
|
|
702
|
+
export type AnyRouter = RouterCore<any, any, any, any, any>
|
|
703
703
|
|
|
704
704
|
export interface ViewTransitionOptions {
|
|
705
705
|
types:
|
|
@@ -753,7 +753,6 @@ export type CreateRouterFn = <
|
|
|
753
753
|
TDefaultStructuralSharingOption extends boolean = false,
|
|
754
754
|
TRouterHistory extends RouterHistory = RouterHistory,
|
|
755
755
|
TDehydrated extends Record<string, any> = Record<string, any>,
|
|
756
|
-
TTransformerConfig = any,
|
|
757
756
|
>(
|
|
758
757
|
options: undefined extends number
|
|
759
758
|
? 'strictNullChecks must be enabled in tsconfig.json'
|
|
@@ -762,16 +761,14 @@ export type CreateRouterFn = <
|
|
|
762
761
|
TTrailingSlashOption,
|
|
763
762
|
TDefaultStructuralSharingOption,
|
|
764
763
|
TRouterHistory,
|
|
765
|
-
TDehydrated
|
|
766
|
-
TTransformerConfig
|
|
764
|
+
TDehydrated
|
|
767
765
|
>,
|
|
768
766
|
) => RouterCore<
|
|
769
767
|
TRouteTree,
|
|
770
768
|
TTrailingSlashOption,
|
|
771
769
|
TDefaultStructuralSharingOption,
|
|
772
770
|
TRouterHistory,
|
|
773
|
-
TDehydrated
|
|
774
|
-
TTransformerConfig
|
|
771
|
+
TDehydrated
|
|
775
772
|
>
|
|
776
773
|
|
|
777
774
|
export class RouterCore<
|
|
@@ -780,7 +777,6 @@ export class RouterCore<
|
|
|
780
777
|
in out TDefaultStructuralSharingOption extends boolean,
|
|
781
778
|
in out TRouterHistory extends RouterHistory = RouterHistory,
|
|
782
779
|
in out TDehydrated extends Record<string, any> = Record<string, any>,
|
|
783
|
-
in out TTransformerConfig = any,
|
|
784
780
|
> {
|
|
785
781
|
// Option-independent properties
|
|
786
782
|
tempLocationKey: string | undefined = `${Math.round(
|
|
@@ -802,8 +798,7 @@ export class RouterCore<
|
|
|
802
798
|
TTrailingSlashOption,
|
|
803
799
|
TDefaultStructuralSharingOption,
|
|
804
800
|
TRouterHistory,
|
|
805
|
-
TDehydrated
|
|
806
|
-
TTransformerConfig
|
|
801
|
+
TDehydrated
|
|
807
802
|
>,
|
|
808
803
|
'stringifySearch' | 'parseSearch' | 'context'
|
|
809
804
|
>
|
|
@@ -826,8 +821,7 @@ export class RouterCore<
|
|
|
826
821
|
TTrailingSlashOption,
|
|
827
822
|
TDefaultStructuralSharingOption,
|
|
828
823
|
TRouterHistory,
|
|
829
|
-
TDehydrated
|
|
830
|
-
TTransformerConfig
|
|
824
|
+
TDehydrated
|
|
831
825
|
>,
|
|
832
826
|
) {
|
|
833
827
|
this.update({
|
|
@@ -865,8 +859,7 @@ export class RouterCore<
|
|
|
865
859
|
TTrailingSlashOption,
|
|
866
860
|
TDefaultStructuralSharingOption,
|
|
867
861
|
TRouterHistory,
|
|
868
|
-
TDehydrated
|
|
869
|
-
TTransformerConfig
|
|
862
|
+
TDehydrated
|
|
870
863
|
> = (newOptions) => {
|
|
871
864
|
if (newOptions.notFoundRoute) {
|
|
872
865
|
console.warn(
|
|
@@ -917,7 +910,7 @@ export class RouterCore<
|
|
|
917
910
|
initialEntries: [this.basepath || '/'],
|
|
918
911
|
})
|
|
919
912
|
: createBrowserHistory()) as TRouterHistory)
|
|
920
|
-
this.
|
|
913
|
+
this.updateLatestLocation()
|
|
921
914
|
}
|
|
922
915
|
|
|
923
916
|
if (this.options.routeTree !== this.routeTree) {
|
|
@@ -955,6 +948,13 @@ export class RouterCore<
|
|
|
955
948
|
return this.__store.state
|
|
956
949
|
}
|
|
957
950
|
|
|
951
|
+
updateLatestLocation = () => {
|
|
952
|
+
this.latestLocation = this.parseLocation(
|
|
953
|
+
this.history.location,
|
|
954
|
+
this.latestLocation,
|
|
955
|
+
)
|
|
956
|
+
}
|
|
957
|
+
|
|
958
958
|
buildRouteTree = () => {
|
|
959
959
|
const { routesById, routesByPath, flatRoutes } = processRouteTree({
|
|
960
960
|
routeTree: this.routeTree,
|
|
@@ -1001,8 +1001,8 @@ export class RouterCore<
|
|
|
1001
1001
|
}
|
|
1002
1002
|
|
|
1003
1003
|
parseLocation: ParseLocationFn<TRouteTree> = (
|
|
1004
|
-
previousLocation,
|
|
1005
1004
|
locationToParse,
|
|
1005
|
+
previousLocation,
|
|
1006
1006
|
) => {
|
|
1007
1007
|
const parse = ({
|
|
1008
1008
|
pathname,
|
|
@@ -1023,7 +1023,7 @@ export class RouterCore<
|
|
|
1023
1023
|
}
|
|
1024
1024
|
}
|
|
1025
1025
|
|
|
1026
|
-
const location = parse(locationToParse
|
|
1026
|
+
const location = parse(locationToParse)
|
|
1027
1027
|
|
|
1028
1028
|
const { __tempLocation, __tempKey } = location.state
|
|
1029
1029
|
|
|
@@ -1425,50 +1425,44 @@ export class RouterCore<
|
|
|
1425
1425
|
_buildLocation: true,
|
|
1426
1426
|
})
|
|
1427
1427
|
|
|
1428
|
+
// Now let's find the starting pathname
|
|
1429
|
+
// This should default to the current location if no from is provided
|
|
1428
1430
|
const lastMatch = last(allCurrentLocationMatches)!
|
|
1429
1431
|
|
|
1430
|
-
//
|
|
1431
|
-
//
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
// If the route is changing we need to find the relative fromPath
|
|
1443
|
-
if (dest.unsafeRelative === 'path') {
|
|
1444
|
-
fromPath = currentLocation.pathname
|
|
1445
|
-
} else if (routeIsChanging && dest.from) {
|
|
1446
|
-
fromPath = dest.from
|
|
1447
|
-
|
|
1448
|
-
// do this check only on navigations during test or development
|
|
1449
|
-
if (process.env.NODE_ENV !== 'production' && dest._isNavigate) {
|
|
1450
|
-
const allFromMatches = this.getMatchedRoutes(
|
|
1451
|
-
dest.from,
|
|
1452
|
-
undefined,
|
|
1453
|
-
).matchedRoutes
|
|
1432
|
+
// check that from path exists in the current route tree
|
|
1433
|
+
// do this check only on navigations during test or development
|
|
1434
|
+
if (
|
|
1435
|
+
dest.from &&
|
|
1436
|
+
process.env.NODE_ENV !== 'production' &&
|
|
1437
|
+
dest._isNavigate
|
|
1438
|
+
) {
|
|
1439
|
+
const allFromMatches = this.getMatchedRoutes(
|
|
1440
|
+
dest.from,
|
|
1441
|
+
undefined,
|
|
1442
|
+
).matchedRoutes
|
|
1454
1443
|
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1444
|
+
const matchedFrom = findLast(allCurrentLocationMatches, (d) => {
|
|
1445
|
+
return comparePaths(d.fullPath, dest.from!)
|
|
1446
|
+
})
|
|
1458
1447
|
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1448
|
+
const matchedCurrent = findLast(allFromMatches, (d) => {
|
|
1449
|
+
return comparePaths(d.fullPath, lastMatch.fullPath)
|
|
1450
|
+
})
|
|
1462
1451
|
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
}
|
|
1452
|
+
// for from to be invalid it shouldn't just be unmatched to currentLocation
|
|
1453
|
+
// but the currentLocation should also be unmatched to from
|
|
1454
|
+
if (!matchedFrom && !matchedCurrent) {
|
|
1455
|
+
console.warn(`Could not find match for from: ${dest.from}`)
|
|
1468
1456
|
}
|
|
1469
1457
|
}
|
|
1470
1458
|
|
|
1471
|
-
|
|
1459
|
+
const defaultedFromPath =
|
|
1460
|
+
dest.unsafeRelative === 'path'
|
|
1461
|
+
? currentLocation.pathname
|
|
1462
|
+
: (dest.from ?? lastMatch.fullPath)
|
|
1463
|
+
|
|
1464
|
+
// ensure this includes the basePath if set
|
|
1465
|
+
const fromPath = this.resolvePathWithBase(defaultedFromPath, '.')
|
|
1472
1466
|
|
|
1473
1467
|
// From search should always use the current location
|
|
1474
1468
|
const fromSearch = lastMatch.search
|
|
@@ -1476,6 +1470,7 @@ export class RouterCore<
|
|
|
1476
1470
|
const fromParams = { ...lastMatch.params }
|
|
1477
1471
|
|
|
1478
1472
|
// Resolve the next to
|
|
1473
|
+
// ensure this includes the basePath if set
|
|
1479
1474
|
const nextTo = dest.to
|
|
1480
1475
|
? this.resolvePathWithBase(fromPath, `${dest.to}`)
|
|
1481
1476
|
: this.resolvePathWithBase(fromPath, '.')
|
|
@@ -1814,7 +1809,7 @@ export class RouterCore<
|
|
|
1814
1809
|
beforeLoad = () => {
|
|
1815
1810
|
// Cancel any pending matches
|
|
1816
1811
|
this.cancelMatches()
|
|
1817
|
-
this.
|
|
1812
|
+
this.updateLatestLocation()
|
|
1818
1813
|
|
|
1819
1814
|
if (this.isServer) {
|
|
1820
1815
|
// for SPAs on the initial load, this is handled by the Transitioner
|
|
@@ -1,39 +1,111 @@
|
|
|
1
1
|
import { createPlugin } from 'seroval'
|
|
2
2
|
import { GLOBAL_TSR } from '../constants'
|
|
3
|
-
import type { SerovalNode } from 'seroval'
|
|
3
|
+
import type { Plugin, SerovalNode } from 'seroval'
|
|
4
|
+
import type { Register, SSROption } from '../../router'
|
|
5
|
+
import type { LooseReturnType } from '../../utils'
|
|
6
|
+
import type { AnyRoute, ResolveAllSSR } from '../../route'
|
|
4
7
|
|
|
5
|
-
export type
|
|
8
|
+
export type Serializable =
|
|
9
|
+
| number
|
|
10
|
+
| string
|
|
11
|
+
| boolean
|
|
12
|
+
| null
|
|
13
|
+
| undefined
|
|
14
|
+
| bigint
|
|
15
|
+
| Date
|
|
16
|
+
|
|
17
|
+
export function createSerializationAdapter<
|
|
18
|
+
TInput = unknown,
|
|
19
|
+
TOutput = unknown /* we need to check that this type is actually serializable taking into account all seroval native types and any custom plugin WE=router/start add!!! */,
|
|
20
|
+
>(
|
|
21
|
+
opts: CreateSerializationAdapterOptions<TInput, TOutput>,
|
|
22
|
+
): SerializationAdapter<TInput, TOutput> {
|
|
23
|
+
return opts as unknown as SerializationAdapter<TInput, TOutput>
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface CreateSerializationAdapterOptions<TInput, TOutput> {
|
|
6
27
|
key: string
|
|
7
|
-
test: (value:
|
|
8
|
-
toSerializable: (value: TInput) =>
|
|
9
|
-
fromSerializable: (value:
|
|
28
|
+
test: (value: unknown) => value is TInput
|
|
29
|
+
toSerializable: (value: TInput) => ValidateSerializable<TOutput, Serializable>
|
|
30
|
+
fromSerializable: (value: TOutput) => TInput
|
|
10
31
|
}
|
|
11
32
|
|
|
12
|
-
export type
|
|
33
|
+
export type ValidateSerializable<T, TSerializable> = T extends TSerializable
|
|
34
|
+
? T
|
|
35
|
+
: T extends (...args: Array<any>) => any
|
|
36
|
+
? 'Function is not serializable'
|
|
37
|
+
: T extends Promise<any>
|
|
38
|
+
? ValidateSerializablePromise<T, TSerializable>
|
|
39
|
+
: T extends ReadableStream<any>
|
|
40
|
+
? ValidateReadableStream<T, TSerializable>
|
|
41
|
+
: T extends Set<any>
|
|
42
|
+
? ValidateSerializableSet<T, TSerializable>
|
|
43
|
+
: T extends Map<any, any>
|
|
44
|
+
? ValidateSerializableMap<T, TSerializable>
|
|
45
|
+
: {
|
|
46
|
+
[K in keyof T]: ValidateSerializable<T[K], TSerializable>
|
|
47
|
+
}
|
|
13
48
|
|
|
14
|
-
export
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
49
|
+
export type ValidateSerializablePromise<T, TSerializable> =
|
|
50
|
+
T extends Promise<infer TAwaited>
|
|
51
|
+
? Promise<ValidateSerializable<TAwaited, TSerializable>>
|
|
52
|
+
: never
|
|
53
|
+
|
|
54
|
+
export type ValidateReadableStream<T, TSerializable> =
|
|
55
|
+
T extends ReadableStream<infer TStreamed>
|
|
56
|
+
? ReadableStream<ValidateSerializable<TStreamed, TSerializable>>
|
|
57
|
+
: never
|
|
58
|
+
|
|
59
|
+
export type ValidateSerializableSet<T, TSerializable> =
|
|
60
|
+
T extends Set<infer TItem>
|
|
61
|
+
? Set<ValidateSerializable<TItem, TSerializable>>
|
|
62
|
+
: never
|
|
63
|
+
|
|
64
|
+
export type ValidateSerializableMap<T, TSerializable> =
|
|
65
|
+
T extends Map<infer TKey, infer TValue>
|
|
66
|
+
? Map<
|
|
67
|
+
ValidateSerializable<TKey, TSerializable>,
|
|
68
|
+
ValidateSerializable<TValue, TSerializable>
|
|
69
|
+
>
|
|
70
|
+
: never
|
|
71
|
+
|
|
72
|
+
export type RegisteredReadableStream =
|
|
73
|
+
unknown extends SerializerExtensions['ReadableStream']
|
|
74
|
+
? never
|
|
75
|
+
: SerializerExtensions['ReadableStream']
|
|
76
|
+
|
|
77
|
+
export interface DefaultSerializerExtensions {
|
|
78
|
+
ReadableStream: unknown
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface SerializerExtensions extends DefaultSerializerExtensions {}
|
|
82
|
+
|
|
83
|
+
export interface SerializationAdapter<TInput, TOutput> {
|
|
84
|
+
'~types': SerializationAdapterTypes<TInput, TOutput>
|
|
85
|
+
key: string
|
|
86
|
+
test: (value: unknown) => value is TInput
|
|
87
|
+
toSerializable: (value: TInput) => TOutput
|
|
88
|
+
fromSerializable: (value: TOutput) => TInput
|
|
89
|
+
makePlugin: (options: { didRun: boolean }) => Plugin<TInput, SerovalNode>
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface SerializationAdapterTypes<TInput, TOutput> {
|
|
93
|
+
input: TInput
|
|
94
|
+
output: TOutput
|
|
25
95
|
}
|
|
26
96
|
|
|
27
|
-
export
|
|
28
|
-
|
|
97
|
+
export type AnySerializationAdapter = SerializationAdapter<any, any>
|
|
98
|
+
|
|
99
|
+
export function makeSsrSerovalPlugin<TInput, TOutput>(
|
|
100
|
+
serializationAdapter: SerializationAdapter<TInput, TOutput>,
|
|
29
101
|
options: { didRun: boolean },
|
|
30
102
|
) {
|
|
31
103
|
return createPlugin<TInput, SerovalNode>({
|
|
32
|
-
tag: '$TSR/t/' +
|
|
33
|
-
test:
|
|
104
|
+
tag: '$TSR/t/' + serializationAdapter.key,
|
|
105
|
+
test: serializationAdapter.test,
|
|
34
106
|
parse: {
|
|
35
107
|
stream(value, ctx) {
|
|
36
|
-
return ctx.parse(
|
|
108
|
+
return ctx.parse(serializationAdapter.toSerializable(value))
|
|
37
109
|
},
|
|
38
110
|
},
|
|
39
111
|
serialize(node, ctx) {
|
|
@@ -41,7 +113,7 @@ export function makeSsrSerovalPlugin<TInput, TTransformed>(
|
|
|
41
113
|
return (
|
|
42
114
|
GLOBAL_TSR +
|
|
43
115
|
'.t.get("' +
|
|
44
|
-
|
|
116
|
+
serializationAdapter.key +
|
|
45
117
|
'")(' +
|
|
46
118
|
ctx.serialize(node) +
|
|
47
119
|
')'
|
|
@@ -52,27 +124,92 @@ export function makeSsrSerovalPlugin<TInput, TTransformed>(
|
|
|
52
124
|
})
|
|
53
125
|
}
|
|
54
126
|
|
|
55
|
-
export function makeSerovalPlugin<TInput,
|
|
56
|
-
|
|
127
|
+
export function makeSerovalPlugin<TInput, TOutput>(
|
|
128
|
+
serializationAdapter: SerializationAdapter<TInput, TOutput>,
|
|
57
129
|
) {
|
|
58
130
|
return createPlugin<TInput, SerovalNode>({
|
|
59
|
-
tag: '$TSR/t/' +
|
|
60
|
-
test:
|
|
131
|
+
tag: '$TSR/t/' + serializationAdapter.key,
|
|
132
|
+
test: serializationAdapter.test,
|
|
61
133
|
parse: {
|
|
62
134
|
sync(value, ctx) {
|
|
63
|
-
return ctx.parse(
|
|
135
|
+
return ctx.parse(serializationAdapter.toSerializable(value))
|
|
64
136
|
},
|
|
65
137
|
async async(value, ctx) {
|
|
66
|
-
return await ctx.parse(
|
|
138
|
+
return await ctx.parse(serializationAdapter.toSerializable(value))
|
|
67
139
|
},
|
|
68
140
|
stream(value, ctx) {
|
|
69
|
-
return ctx.parse(
|
|
141
|
+
return ctx.parse(serializationAdapter.toSerializable(value))
|
|
70
142
|
},
|
|
71
143
|
},
|
|
72
144
|
// we don't generate JS code outside of SSR (for now)
|
|
73
145
|
serialize: undefined as never,
|
|
74
146
|
deserialize(node, ctx) {
|
|
75
|
-
return
|
|
147
|
+
return serializationAdapter.fromSerializable(
|
|
148
|
+
ctx.deserialize(node) as TOutput,
|
|
149
|
+
)
|
|
76
150
|
},
|
|
77
151
|
})
|
|
78
152
|
}
|
|
153
|
+
|
|
154
|
+
export type ValidateSerializableInput<
|
|
155
|
+
TRegister extends Register,
|
|
156
|
+
T,
|
|
157
|
+
> = ValidateSerializable<T, RegisteredSerializableInput<TRegister>>
|
|
158
|
+
|
|
159
|
+
export type RegisteredSerializableInput<TRegister extends Register> =
|
|
160
|
+
| (unknown extends RegisteredSerializationAdapters<TRegister>
|
|
161
|
+
? never
|
|
162
|
+
: RegisteredSerializationAdapters<TRegister> extends ReadonlyArray<AnySerializationAdapter>
|
|
163
|
+
? RegisteredSerializationAdapters<TRegister>[number]['~types']['input']
|
|
164
|
+
: never)
|
|
165
|
+
| Serializable
|
|
166
|
+
|
|
167
|
+
export type RegisteredSerializationAdapters<TRegister extends Register> =
|
|
168
|
+
TRegister['config']['~types']['serializationAdapters']
|
|
169
|
+
|
|
170
|
+
export type ValidateSerializableInputResult<
|
|
171
|
+
TRegister extends Register,
|
|
172
|
+
T,
|
|
173
|
+
> = ValidateSerializableResult<T, RegisteredSerializableInput<TRegister>>
|
|
174
|
+
|
|
175
|
+
export type ValidateSerializableResult<T, TSerializable> =
|
|
176
|
+
T extends TSerializable
|
|
177
|
+
? T
|
|
178
|
+
: unknown extends SerializerExtensions['ReadableStream']
|
|
179
|
+
? { [K in keyof T]: ValidateSerializableResult<T[K], TSerializable> }
|
|
180
|
+
: T extends SerializerExtensions['ReadableStream']
|
|
181
|
+
? ReadableStream
|
|
182
|
+
: { [K in keyof T]: ValidateSerializableResult<T[K], TSerializable> }
|
|
183
|
+
|
|
184
|
+
export type RegisteredSSROption<TRegister extends Register> =
|
|
185
|
+
unknown extends TRegister['config']['~types']['defaultSsr']
|
|
186
|
+
? SSROption
|
|
187
|
+
: TRegister['config']['~types']['defaultSsr']
|
|
188
|
+
|
|
189
|
+
export type ValidateSerializableLifecycleResult<
|
|
190
|
+
TRegister extends Register,
|
|
191
|
+
TParentRoute extends AnyRoute,
|
|
192
|
+
TSSR,
|
|
193
|
+
TFn,
|
|
194
|
+
> = false extends TRegister['ssr']
|
|
195
|
+
? any
|
|
196
|
+
: ValidateSerializableLifecycleResultSSR<
|
|
197
|
+
TRegister,
|
|
198
|
+
TParentRoute,
|
|
199
|
+
TSSR,
|
|
200
|
+
TFn
|
|
201
|
+
> extends infer TInput
|
|
202
|
+
? TInput
|
|
203
|
+
: never
|
|
204
|
+
|
|
205
|
+
export type ValidateSerializableLifecycleResultSSR<
|
|
206
|
+
TRegister extends Register,
|
|
207
|
+
TParentRoute extends AnyRoute,
|
|
208
|
+
TSSR,
|
|
209
|
+
TFn,
|
|
210
|
+
> =
|
|
211
|
+
ResolveAllSSR<TParentRoute, TSSR> extends false
|
|
212
|
+
? any
|
|
213
|
+
: RegisteredSSROption<TRegister> extends false
|
|
214
|
+
? any
|
|
215
|
+
: ValidateSerializableInput<TRegister, LooseReturnType<TFn>>
|
package/src/ssr/server.ts
CHANGED
package/src/ssr/ssr-client.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type { AnyRouteMatch, MakeRouteMatch } from '../Matches'
|
|
|
5
5
|
import type { AnyRouter } from '../router'
|
|
6
6
|
import type { Manifest } from '../manifest'
|
|
7
7
|
import type { RouteContextOptions } from '../route'
|
|
8
|
-
import type {
|
|
8
|
+
import type { AnySerializationAdapter } from './serializer/transformer'
|
|
9
9
|
import type { GLOBAL_TSR } from './constants'
|
|
10
10
|
|
|
11
11
|
declare global {
|
|
@@ -63,7 +63,7 @@ export async function hydrate(router: AnyRouter): Promise<any> {
|
|
|
63
63
|
)
|
|
64
64
|
|
|
65
65
|
const serializationAdapters = router.options.serializationAdapters as
|
|
66
|
-
| Array<
|
|
66
|
+
| Array<AnySerializationAdapter>
|
|
67
67
|
| undefined
|
|
68
68
|
|
|
69
69
|
if (serializationAdapters?.length) {
|
package/src/ssr/ssr-server.ts
CHANGED
|
@@ -10,7 +10,7 @@ import type { DehydratedMatch } from './ssr-client'
|
|
|
10
10
|
import type { DehydratedRouter } from './client'
|
|
11
11
|
import type { AnyRouteMatch } from '../Matches'
|
|
12
12
|
import type { Manifest } from '../manifest'
|
|
13
|
-
import type {
|
|
13
|
+
import type { AnySerializationAdapter } from './serializer/transformer'
|
|
14
14
|
|
|
15
15
|
declare module '../router' {
|
|
16
16
|
interface ServerSsr {
|
|
@@ -109,7 +109,7 @@ export function attachRouterServerSsrUtils(
|
|
|
109
109
|
const plugins =
|
|
110
110
|
(
|
|
111
111
|
router.options.serializationAdapters as
|
|
112
|
-
| Array<
|
|
112
|
+
| Array<AnySerializationAdapter>
|
|
113
113
|
| undefined
|
|
114
114
|
)?.map((t) => makeSsrSerovalPlugin(t, trackPlugins)) ?? []
|
|
115
115
|
crossSerializeStream(dehydratedRouter, {
|