@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.
Files changed (61) hide show
  1. package/dist/cjs/Matches.cjs.map +1 -1
  2. package/dist/cjs/Matches.d.cts +2 -2
  3. package/dist/cjs/config.cjs +10 -0
  4. package/dist/cjs/config.cjs.map +1 -0
  5. package/dist/cjs/config.d.cts +17 -0
  6. package/dist/cjs/fileRoute.d.cts +3 -2
  7. package/dist/cjs/index.cjs +2 -0
  8. package/dist/cjs/index.cjs.map +1 -1
  9. package/dist/cjs/index.d.cts +3 -2
  10. package/dist/cjs/load-matches.cjs.map +1 -1
  11. package/dist/cjs/route.cjs.map +1 -1
  12. package/dist/cjs/route.d.cts +42 -37
  13. package/dist/cjs/router.cjs +25 -26
  14. package/dist/cjs/router.cjs.map +1 -1
  15. package/dist/cjs/router.d.cts +23 -15
  16. package/dist/cjs/scroll-restoration.cjs +12 -4
  17. package/dist/cjs/scroll-restoration.cjs.map +1 -1
  18. package/dist/cjs/scroll-restoration.d.cts +1 -1
  19. package/dist/cjs/ssr/serializer/transformer.cjs +14 -12
  20. package/dist/cjs/ssr/serializer/transformer.cjs.map +1 -1
  21. package/dist/cjs/ssr/serializer/transformer.d.cts +53 -15
  22. package/dist/cjs/ssr/server.d.cts +5 -0
  23. package/dist/cjs/ssr/ssr-client.cjs.map +1 -1
  24. package/dist/cjs/ssr/ssr-server.cjs.map +1 -1
  25. package/dist/esm/Matches.d.ts +2 -2
  26. package/dist/esm/Matches.js.map +1 -1
  27. package/dist/esm/config.d.ts +17 -0
  28. package/dist/esm/config.js +10 -0
  29. package/dist/esm/config.js.map +1 -0
  30. package/dist/esm/fileRoute.d.ts +3 -2
  31. package/dist/esm/index.d.ts +3 -2
  32. package/dist/esm/index.js +2 -0
  33. package/dist/esm/index.js.map +1 -1
  34. package/dist/esm/load-matches.js.map +1 -1
  35. package/dist/esm/route.d.ts +42 -37
  36. package/dist/esm/route.js.map +1 -1
  37. package/dist/esm/router.d.ts +23 -15
  38. package/dist/esm/router.js +25 -26
  39. package/dist/esm/router.js.map +1 -1
  40. package/dist/esm/scroll-restoration.d.ts +1 -1
  41. package/dist/esm/scroll-restoration.js +12 -4
  42. package/dist/esm/scroll-restoration.js.map +1 -1
  43. package/dist/esm/ssr/serializer/transformer.d.ts +53 -15
  44. package/dist/esm/ssr/serializer/transformer.js +14 -12
  45. package/dist/esm/ssr/serializer/transformer.js.map +1 -1
  46. package/dist/esm/ssr/server.d.ts +5 -0
  47. package/dist/esm/ssr/ssr-client.js.map +1 -1
  48. package/dist/esm/ssr/ssr-server.js.map +1 -1
  49. package/package.json +1 -1
  50. package/src/Matches.ts +2 -2
  51. package/src/config.ts +42 -0
  52. package/src/fileRoute.ts +15 -3
  53. package/src/index.ts +13 -1
  54. package/src/load-matches.ts +2 -2
  55. package/src/route.ts +136 -33
  56. package/src/router.ts +63 -66
  57. package/src/scroll-restoration.ts +17 -5
  58. package/src/ssr/serializer/transformer.ts +168 -31
  59. package/src/ssr/server.ts +6 -0
  60. package/src/ssr/ssr-client.ts +2 -2
  61. 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?: boolean | 'data-only'
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?: boolean
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?: TTransformerConfig
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, 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.latestLocation = this.parseLocation()
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 ?? this.history.location)
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
- // First let's find the starting pathname
1429
- // By default, start with the current location
1430
- let fromPath = this.resolvePathWithBase(lastMatch.fullPath, '.')
1431
- const toPath = dest.to
1432
- ? this.resolvePathWithBase(fromPath, `${dest.to}`)
1433
- : this.resolvePathWithBase(fromPath, '.')
1434
-
1435
- const routeIsChanging =
1436
- !!dest.to &&
1437
- !comparePaths(dest.to.toString(), fromPath) &&
1438
- !comparePaths(toPath, fromPath)
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
- const matchedFrom = findLast(allCurrentLocationMatches, (d) => {
1454
- return comparePaths(d.fullPath, fromPath)
1455
- })
1444
+ const matchedFrom = findLast(allCurrentLocationMatches, (d) => {
1445
+ return comparePaths(d.fullPath, dest.from!)
1446
+ })
1456
1447
 
1457
- const matchedCurrent = findLast(allFromMatches, (d) => {
1458
- return comparePaths(d.fullPath, currentLocation.pathname)
1459
- })
1448
+ const matchedCurrent = findLast(allFromMatches, (d) => {
1449
+ return comparePaths(d.fullPath, lastMatch.fullPath)
1450
+ })
1460
1451
 
1461
- // for from to be invalid it shouldn't just be unmatched to currentLocation
1462
- // but the currentLocation should also be unmatched to from
1463
- if (!matchedFrom && !matchedCurrent) {
1464
- console.warn(`Could not find match for from: ${fromPath}`)
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
- fromPath = this.resolvePathWithBase(fromPath, '.')
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.latestLocation = this.parseLocation(this.latestLocation)
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 | undefined {
50
+ function createScrollRestorationCache(): ScrollRestorationCache | null {
51
51
  const safeSessionStorage = getSafeSessionStorage()
52
52
  if (!safeSessionStorage) {
53
- return undefined
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?.key
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 === undefined) {
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 (typeof document === 'undefined' || router.isScrollRestorationSetup) {
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 Transformer<TInput, TTransformed> = {
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: any) => value is TInput
8
- toSerializable: (value: TInput) => TTransformed
9
- fromSerializable: (value: TTransformed) => TInput
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 AnyTransformer = Transformer<any, any>
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 function createSerializationAdapter<
15
- TKey extends string,
16
- TInput,
17
- TTransformed /* 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!!! */,
18
- >(opts: {
19
- key: TKey
20
- test: (value: any) => value is TInput
21
- toSerializable: (value: TInput) => TTransformed
22
- fromSerializable: (value: TTransformed) => TInput
23
- }): Transformer<TInput, TTransformed> {
24
- return opts
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 function makeSsrSerovalPlugin<TInput, TTransformed>(
28
- transformer: Transformer<TInput, TTransformed>,
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/' + transformer.key,
33
- test: transformer.test,
104
+ tag: '$TSR/t/' + serializationAdapter.key,
105
+ test: serializationAdapter.test,
34
106
  parse: {
35
107
  stream(value, ctx) {
36
- return ctx.parse(transformer.toSerializable(value))
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
- transformer.key +
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, TTransformed>(
56
- transformer: Transformer<TInput, TTransformed>,
127
+ export function makeSerovalPlugin<TInput, TOutput>(
128
+ serializationAdapter: SerializationAdapter<TInput, TOutput>,
57
129
  ) {
58
130
  return createPlugin<TInput, SerovalNode>({
59
- tag: '$TSR/t/' + transformer.key,
60
- test: transformer.test,
131
+ tag: '$TSR/t/' + serializationAdapter.key,
132
+ test: serializationAdapter.test,
61
133
  parse: {
62
134
  sync(value, ctx) {
63
- return ctx.parse(transformer.toSerializable(value))
135
+ return ctx.parse(serializationAdapter.toSerializable(value))
64
136
  },
65
137
  async async(value, ctx) {
66
- return await ctx.parse(transformer.toSerializable(value))
138
+ return await ctx.parse(serializationAdapter.toSerializable(value))
67
139
  },
68
140
  stream(value, ctx) {
69
- return ctx.parse(transformer.toSerializable(value))
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 transformer.fromSerializable(ctx.deserialize(node) as TTransformed)
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
@@ -8,3 +8,9 @@ export {
8
8
  transformReadableStreamWithRouter,
9
9
  } from './transformStreamWithRouter'
10
10
  export { attachRouterServerSsrUtils } from './ssr-server'
11
+
12
+ declare module '../router' {
13
+ export interface Register {
14
+ ssr: true
15
+ }
16
+ }
@@ -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 { AnyTransformer } from './serializer/transformer'
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<AnyTransformer>
66
+ | Array<AnySerializationAdapter>
67
67
  | undefined
68
68
 
69
69
  if (serializationAdapters?.length) {
@@ -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 { AnyTransformer } from './serializer/transformer'
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<AnyTransformer>
112
+ | Array<AnySerializationAdapter>
113
113
  | undefined
114
114
  )?.map((t) => makeSsrSerovalPlugin(t, trackPlugins)) ?? []
115
115
  crossSerializeStream(dehydratedRouter, {