@tanstack/router-core 1.132.0-alpha.3 → 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 +23 -15
- package/dist/cjs/scroll-restoration.cjs +12 -4
- package/dist/cjs/scroll-restoration.cjs.map +1 -1
- package/dist/cjs/scroll-restoration.d.cts +1 -1
- 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 +23 -15
- package/dist/esm/router.js +25 -26
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/scroll-restoration.d.ts +1 -1
- package/dist/esm/scroll-restoration.js +12 -4
- package/dist/esm/scroll-restoration.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 +63 -66
- package/src/scroll-restoration.ts +17 -5
- 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
|
/**
|
|
@@ -391,7 +396,9 @@ export interface RouterOptions<
|
|
|
391
396
|
*
|
|
392
397
|
* @default false
|
|
393
398
|
*/
|
|
394
|
-
scrollRestoration?:
|
|
399
|
+
scrollRestoration?:
|
|
400
|
+
| boolean
|
|
401
|
+
| ((opts: { location: ParsedLocation }) => boolean)
|
|
395
402
|
|
|
396
403
|
/**
|
|
397
404
|
* A function that will be called to get the key for the scroll restoration cache.
|
|
@@ -423,7 +430,7 @@ export interface RouterOptions<
|
|
|
423
430
|
*/
|
|
424
431
|
disableGlobalCatchBoundary?: boolean
|
|
425
432
|
|
|
426
|
-
serializationAdapters?:
|
|
433
|
+
serializationAdapters?: ReadonlyArray<AnySerializationAdapter>
|
|
427
434
|
}
|
|
428
435
|
|
|
429
436
|
export interface RouterState<
|
|
@@ -532,15 +539,13 @@ export type RouterConstructorOptions<
|
|
|
532
539
|
TDefaultStructuralSharingOption extends boolean,
|
|
533
540
|
TRouterHistory extends RouterHistory,
|
|
534
541
|
TDehydrated extends Record<string, any>,
|
|
535
|
-
TTransformerConfig,
|
|
536
542
|
> = Omit<
|
|
537
543
|
RouterOptions<
|
|
538
544
|
TRouteTree,
|
|
539
545
|
TTrailingSlashOption,
|
|
540
546
|
TDefaultStructuralSharingOption,
|
|
541
547
|
TRouterHistory,
|
|
542
|
-
TDehydrated
|
|
543
|
-
TTransformerConfig
|
|
548
|
+
TDehydrated
|
|
544
549
|
>,
|
|
545
550
|
'context'
|
|
546
551
|
> &
|
|
@@ -600,15 +605,13 @@ export type UpdateFn<
|
|
|
600
605
|
TDefaultStructuralSharingOption extends boolean,
|
|
601
606
|
TRouterHistory extends RouterHistory,
|
|
602
607
|
TDehydrated extends Record<string, any>,
|
|
603
|
-
TTransformerConfig extends any,
|
|
604
608
|
> = (
|
|
605
609
|
newOptions: RouterConstructorOptions<
|
|
606
610
|
TRouteTree,
|
|
607
611
|
TTrailingSlashOption,
|
|
608
612
|
TDefaultStructuralSharingOption,
|
|
609
613
|
TRouterHistory,
|
|
610
|
-
TDehydrated
|
|
611
|
-
TTransformerConfig
|
|
614
|
+
TDehydrated
|
|
612
615
|
>,
|
|
613
616
|
) => void
|
|
614
617
|
|
|
@@ -619,8 +622,8 @@ export type InvalidateFn<TRouter extends AnyRouter> = (opts?: {
|
|
|
619
622
|
}) => Promise<void>
|
|
620
623
|
|
|
621
624
|
export type ParseLocationFn<TRouteTree extends AnyRoute> = (
|
|
625
|
+
locationToParse: HistoryLocation,
|
|
622
626
|
previousLocation?: ParsedLocation<FullSearchSchema<TRouteTree>>,
|
|
623
|
-
locationToParse?: HistoryLocation,
|
|
624
627
|
) => ParsedLocation<FullSearchSchema<TRouteTree>>
|
|
625
628
|
|
|
626
629
|
export type GetMatchRoutesFn = (
|
|
@@ -693,11 +696,10 @@ export type AnyRouterWithContext<TContext> = RouterCore<
|
|
|
693
696
|
any,
|
|
694
697
|
any,
|
|
695
698
|
any,
|
|
696
|
-
any,
|
|
697
699
|
any
|
|
698
700
|
>
|
|
699
701
|
|
|
700
|
-
export type AnyRouter = RouterCore<any, any, any, any, any
|
|
702
|
+
export type AnyRouter = RouterCore<any, any, any, any, any>
|
|
701
703
|
|
|
702
704
|
export interface ViewTransitionOptions {
|
|
703
705
|
types:
|
|
@@ -751,7 +753,6 @@ export type CreateRouterFn = <
|
|
|
751
753
|
TDefaultStructuralSharingOption extends boolean = false,
|
|
752
754
|
TRouterHistory extends RouterHistory = RouterHistory,
|
|
753
755
|
TDehydrated extends Record<string, any> = Record<string, any>,
|
|
754
|
-
TTransformerConfig = any,
|
|
755
756
|
>(
|
|
756
757
|
options: undefined extends number
|
|
757
758
|
? 'strictNullChecks must be enabled in tsconfig.json'
|
|
@@ -760,16 +761,14 @@ export type CreateRouterFn = <
|
|
|
760
761
|
TTrailingSlashOption,
|
|
761
762
|
TDefaultStructuralSharingOption,
|
|
762
763
|
TRouterHistory,
|
|
763
|
-
TDehydrated
|
|
764
|
-
TTransformerConfig
|
|
764
|
+
TDehydrated
|
|
765
765
|
>,
|
|
766
766
|
) => RouterCore<
|
|
767
767
|
TRouteTree,
|
|
768
768
|
TTrailingSlashOption,
|
|
769
769
|
TDefaultStructuralSharingOption,
|
|
770
770
|
TRouterHistory,
|
|
771
|
-
TDehydrated
|
|
772
|
-
TTransformerConfig
|
|
771
|
+
TDehydrated
|
|
773
772
|
>
|
|
774
773
|
|
|
775
774
|
export class RouterCore<
|
|
@@ -778,7 +777,6 @@ export class RouterCore<
|
|
|
778
777
|
in out TDefaultStructuralSharingOption extends boolean,
|
|
779
778
|
in out TRouterHistory extends RouterHistory = RouterHistory,
|
|
780
779
|
in out TDehydrated extends Record<string, any> = Record<string, any>,
|
|
781
|
-
in out TTransformerConfig = any,
|
|
782
780
|
> {
|
|
783
781
|
// Option-independent properties
|
|
784
782
|
tempLocationKey: string | undefined = `${Math.round(
|
|
@@ -800,8 +798,7 @@ export class RouterCore<
|
|
|
800
798
|
TTrailingSlashOption,
|
|
801
799
|
TDefaultStructuralSharingOption,
|
|
802
800
|
TRouterHistory,
|
|
803
|
-
TDehydrated
|
|
804
|
-
TTransformerConfig
|
|
801
|
+
TDehydrated
|
|
805
802
|
>,
|
|
806
803
|
'stringifySearch' | 'parseSearch' | 'context'
|
|
807
804
|
>
|
|
@@ -824,8 +821,7 @@ export class RouterCore<
|
|
|
824
821
|
TTrailingSlashOption,
|
|
825
822
|
TDefaultStructuralSharingOption,
|
|
826
823
|
TRouterHistory,
|
|
827
|
-
TDehydrated
|
|
828
|
-
TTransformerConfig
|
|
824
|
+
TDehydrated
|
|
829
825
|
>,
|
|
830
826
|
) {
|
|
831
827
|
this.update({
|
|
@@ -863,8 +859,7 @@ export class RouterCore<
|
|
|
863
859
|
TTrailingSlashOption,
|
|
864
860
|
TDefaultStructuralSharingOption,
|
|
865
861
|
TRouterHistory,
|
|
866
|
-
TDehydrated
|
|
867
|
-
TTransformerConfig
|
|
862
|
+
TDehydrated
|
|
868
863
|
> = (newOptions) => {
|
|
869
864
|
if (newOptions.notFoundRoute) {
|
|
870
865
|
console.warn(
|
|
@@ -915,7 +910,7 @@ export class RouterCore<
|
|
|
915
910
|
initialEntries: [this.basepath || '/'],
|
|
916
911
|
})
|
|
917
912
|
: createBrowserHistory()) as TRouterHistory)
|
|
918
|
-
this.
|
|
913
|
+
this.updateLatestLocation()
|
|
919
914
|
}
|
|
920
915
|
|
|
921
916
|
if (this.options.routeTree !== this.routeTree) {
|
|
@@ -949,10 +944,17 @@ export class RouterCore<
|
|
|
949
944
|
}
|
|
950
945
|
}
|
|
951
946
|
|
|
952
|
-
get state() {
|
|
947
|
+
get state(): RouterState<TRouteTree> {
|
|
953
948
|
return this.__store.state
|
|
954
949
|
}
|
|
955
950
|
|
|
951
|
+
updateLatestLocation = () => {
|
|
952
|
+
this.latestLocation = this.parseLocation(
|
|
953
|
+
this.history.location,
|
|
954
|
+
this.latestLocation,
|
|
955
|
+
)
|
|
956
|
+
}
|
|
957
|
+
|
|
956
958
|
buildRouteTree = () => {
|
|
957
959
|
const { routesById, routesByPath, flatRoutes } = processRouteTree({
|
|
958
960
|
routeTree: this.routeTree,
|
|
@@ -999,8 +1001,8 @@ export class RouterCore<
|
|
|
999
1001
|
}
|
|
1000
1002
|
|
|
1001
1003
|
parseLocation: ParseLocationFn<TRouteTree> = (
|
|
1002
|
-
previousLocation,
|
|
1003
1004
|
locationToParse,
|
|
1005
|
+
previousLocation,
|
|
1004
1006
|
) => {
|
|
1005
1007
|
const parse = ({
|
|
1006
1008
|
pathname,
|
|
@@ -1021,7 +1023,7 @@ export class RouterCore<
|
|
|
1021
1023
|
}
|
|
1022
1024
|
}
|
|
1023
1025
|
|
|
1024
|
-
const location = parse(locationToParse
|
|
1026
|
+
const location = parse(locationToParse)
|
|
1025
1027
|
|
|
1026
1028
|
const { __tempLocation, __tempKey } = location.state
|
|
1027
1029
|
|
|
@@ -1423,50 +1425,44 @@ export class RouterCore<
|
|
|
1423
1425
|
_buildLocation: true,
|
|
1424
1426
|
})
|
|
1425
1427
|
|
|
1428
|
+
// Now let's find the starting pathname
|
|
1429
|
+
// This should default to the current location if no from is provided
|
|
1426
1430
|
const lastMatch = last(allCurrentLocationMatches)!
|
|
1427
1431
|
|
|
1428
|
-
//
|
|
1429
|
-
//
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
// If the route is changing we need to find the relative fromPath
|
|
1441
|
-
if (dest.unsafeRelative === 'path') {
|
|
1442
|
-
fromPath = currentLocation.pathname
|
|
1443
|
-
} else if (routeIsChanging && dest.from) {
|
|
1444
|
-
fromPath = dest.from
|
|
1445
|
-
|
|
1446
|
-
// do this check only on navigations during test or development
|
|
1447
|
-
if (process.env.NODE_ENV !== 'production' && dest._isNavigate) {
|
|
1448
|
-
const allFromMatches = this.getMatchedRoutes(
|
|
1449
|
-
dest.from,
|
|
1450
|
-
undefined,
|
|
1451
|
-
).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
|
|
1452
1443
|
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1444
|
+
const matchedFrom = findLast(allCurrentLocationMatches, (d) => {
|
|
1445
|
+
return comparePaths(d.fullPath, dest.from!)
|
|
1446
|
+
})
|
|
1456
1447
|
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1448
|
+
const matchedCurrent = findLast(allFromMatches, (d) => {
|
|
1449
|
+
return comparePaths(d.fullPath, lastMatch.fullPath)
|
|
1450
|
+
})
|
|
1460
1451
|
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
}
|
|
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}`)
|
|
1466
1456
|
}
|
|
1467
1457
|
}
|
|
1468
1458
|
|
|
1469
|
-
|
|
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, '.')
|
|
1470
1466
|
|
|
1471
1467
|
// From search should always use the current location
|
|
1472
1468
|
const fromSearch = lastMatch.search
|
|
@@ -1474,6 +1470,7 @@ export class RouterCore<
|
|
|
1474
1470
|
const fromParams = { ...lastMatch.params }
|
|
1475
1471
|
|
|
1476
1472
|
// Resolve the next to
|
|
1473
|
+
// ensure this includes the basePath if set
|
|
1477
1474
|
const nextTo = dest.to
|
|
1478
1475
|
? this.resolvePathWithBase(fromPath, `${dest.to}`)
|
|
1479
1476
|
: this.resolvePathWithBase(fromPath, '.')
|
|
@@ -1812,7 +1809,7 @@ export class RouterCore<
|
|
|
1812
1809
|
beforeLoad = () => {
|
|
1813
1810
|
// Cancel any pending matches
|
|
1814
1811
|
this.cancelMatches()
|
|
1815
|
-
this.
|
|
1812
|
+
this.updateLatestLocation()
|
|
1816
1813
|
|
|
1817
1814
|
if (this.isServer) {
|
|
1818
1815
|
// for SPAs on the initial load, this is handled by the Transitioner
|
|
@@ -47,10 +47,10 @@ const throttle = (fn: (...args: Array<any>) => void, wait: number) => {
|
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
function createScrollRestorationCache(): ScrollRestorationCache |
|
|
50
|
+
function createScrollRestorationCache(): ScrollRestorationCache | null {
|
|
51
51
|
const safeSessionStorage = getSafeSessionStorage()
|
|
52
52
|
if (!safeSessionStorage) {
|
|
53
|
-
return
|
|
53
|
+
return null
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
const persistedState = safeSessionStorage.getItem(storageKey)
|
|
@@ -125,7 +125,7 @@ export function restoreScroll({
|
|
|
125
125
|
return
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
const resolvedKey = key || window.history.state?.
|
|
128
|
+
const resolvedKey = key || window.history.state?.__TSR_key
|
|
129
129
|
const elementEntries = byKey[resolvedKey]
|
|
130
130
|
|
|
131
131
|
//
|
|
@@ -201,7 +201,7 @@ export function restoreScroll({
|
|
|
201
201
|
}
|
|
202
202
|
|
|
203
203
|
export function setupScrollRestoration(router: AnyRouter, force?: boolean) {
|
|
204
|
-
if (scrollRestorationCache
|
|
204
|
+
if (!scrollRestorationCache && !router.isServer) {
|
|
205
205
|
return
|
|
206
206
|
}
|
|
207
207
|
const shouldScrollRestoration =
|
|
@@ -211,7 +211,11 @@ export function setupScrollRestoration(router: AnyRouter, force?: boolean) {
|
|
|
211
211
|
router.isScrollRestoring = true
|
|
212
212
|
}
|
|
213
213
|
|
|
214
|
-
if (
|
|
214
|
+
if (
|
|
215
|
+
router.isServer ||
|
|
216
|
+
router.isScrollRestorationSetup ||
|
|
217
|
+
!scrollRestorationCache
|
|
218
|
+
) {
|
|
215
219
|
return
|
|
216
220
|
}
|
|
217
221
|
|
|
@@ -324,6 +328,14 @@ export function setupScrollRestoration(router: AnyRouter, force?: boolean) {
|
|
|
324
328
|
router.resetNextScroll = true
|
|
325
329
|
return
|
|
326
330
|
}
|
|
331
|
+
if (typeof router.options.scrollRestoration === 'function') {
|
|
332
|
+
const shouldRestore = router.options.scrollRestoration({
|
|
333
|
+
location: router.latestLocation,
|
|
334
|
+
})
|
|
335
|
+
if (!shouldRestore) {
|
|
336
|
+
return
|
|
337
|
+
}
|
|
338
|
+
}
|
|
327
339
|
|
|
328
340
|
restoreScroll({
|
|
329
341
|
storageKey,
|
|
@@ -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, {
|