11import {
22 createRawStreamDeserializePlugin ,
3- encode ,
3+ hasKeys ,
44 invariant ,
55 isNotFound ,
66 parseRedirect ,
77} from '@tanstack/router-core'
8- import { fromCrossJSON , toJSONAsync } from 'seroval'
9- import { getDefaultSerovalPlugins } from '../getDefaultSerovalPlugins'
8+ import { fromCrossJSON } from 'seroval'
9+ import { getDefaultSerovalPlugins as getSerovalPlugins } from '../getDefaultSerovalPlugins'
1010import {
1111 TSS_CONTENT_TYPE_FRAMED ,
1212 TSS_FORMDATA_CONTEXT ,
1313 X_TSS_RAW_RESPONSE ,
1414 X_TSS_SERIALIZED ,
1515 validateFramedProtocolVersion ,
1616} from '../constants'
17+ import {
18+ buildServerFnUrlFromBase ,
19+ serializeServerFnPayload ,
20+ serializeServerFnPayloadValue ,
21+ } from './serverFnUrl'
1722import { createFrameDecoder } from './frame-decoder'
1823import type { FunctionMiddlewareClientFnOptions } from '../createMiddleware'
19- import type { Plugin as SerovalPlugin } from 'seroval'
20-
21- let serovalPlugins : Array < SerovalPlugin < any , any > > | null = null
2224
2325/**
2426 * Current async post-processing context for deserialization.
@@ -84,19 +86,6 @@ async function awaitPostProcessPromises(
8486 }
8587}
8688
87- /**
88- * Checks if an object has at least one own enumerable property.
89- * More efficient than Object.keys(obj).length > 0 as it short-circuits on first property.
90- */
91- const hop = Object . prototype . hasOwnProperty
92- function hasOwnProperties ( obj : object ) : boolean {
93- for ( const _ in obj ) {
94- if ( hop . call ( obj , _ ) ) {
95- return true
96- }
97- }
98- return false
99- }
10089// caller =>
10190// serverFnFetcher =>
10291// client =>
@@ -112,9 +101,6 @@ export async function serverFnFetcher(
112101 args : Array < any > ,
113102 handler : ( url : string , requestInit : RequestInit ) => Promise < Response > ,
114103) {
115- if ( ! serovalPlugins ) {
116- serovalPlugins = getDefaultSerovalPlugins ( )
117- }
118104 const _first = args [ 0 ]
119105
120106 const first = _first as FunctionMiddlewareClientFnOptions < any , any , any > & {
@@ -139,20 +125,7 @@ export async function serverFnFetcher(
139125
140126 // If the method is GET, we need to move the payload to the query string
141127 if ( first . method === 'GET' ) {
142- if ( type === 'formData' ) {
143- throw new Error ( 'FormData is not supported with GET requests' )
144- }
145- const serializedPayload = await serializePayload ( first )
146- if ( serializedPayload !== undefined ) {
147- const encodedPayload = encode ( {
148- payload : serializedPayload ,
149- } )
150- if ( url . includes ( '?' ) ) {
151- url += `&${ encodedPayload } `
152- } else {
153- url += `?${ encodedPayload } `
154- }
155- }
128+ url = await buildServerFnUrlFromBase ( url , first )
156129 }
157130
158131 let body = undefined
@@ -174,49 +147,21 @@ export async function serverFnFetcher(
174147 )
175148}
176149
177- async function serializePayload (
178- opts : FunctionMiddlewareClientFnOptions < any , any , any > ,
179- ) : Promise < string | undefined > {
180- let payloadAvailable = false
181- const payloadToSerialize : any = { }
182- if ( opts . data !== undefined ) {
183- payloadAvailable = true
184- payloadToSerialize [ 'data' ] = opts . data
185- }
186-
187- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
188- if ( opts . context && hasOwnProperties ( opts . context ) ) {
189- payloadAvailable = true
190- payloadToSerialize [ 'context' ] = opts . context
191- }
192-
193- if ( payloadAvailable ) {
194- return serialize ( payloadToSerialize )
195- }
196- return undefined
197- }
198-
199- async function serialize ( data : any ) {
200- return JSON . stringify (
201- await Promise . resolve ( toJSONAsync ( data , { plugins : serovalPlugins ! } ) ) ,
202- )
203- }
204-
205150async function getFetchBody (
206151 opts : FunctionMiddlewareClientFnOptions < any , any , any > ,
207152) : Promise < { body : FormData | string ; contentType ?: string } | undefined > {
208153 if ( opts . data instanceof FormData ) {
209154 let serializedContext = undefined
210155 // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
211- if ( opts . context && hasOwnProperties ( opts . context ) ) {
212- serializedContext = await serialize ( opts . context )
156+ if ( opts . context && hasKeys ( opts . context ) ) {
157+ serializedContext = await serializeServerFnPayloadValue ( opts . context )
213158 }
214159 if ( serializedContext !== undefined ) {
215160 opts . data . set ( TSS_FORMDATA_CONTEXT , serializedContext )
216161 }
217162 return { body : opts . data }
218163 }
219- const serializedBody = await serializePayload ( opts )
164+ const serializedBody = await serializeServerFnPayload ( opts )
220165 if ( serializedBody ) {
221166 return { body : serializedBody , contentType : 'application/json' }
222167 }
@@ -281,7 +226,7 @@ async function getResponse(fn: () => Promise<Response>) {
281226 // Create deserialize plugin that wires up the raw streams
282227 const rawStreamPlugin =
283228 createRawStreamDeserializePlugin ( getOrCreateStream )
284- const plugins = [ rawStreamPlugin , ...( serovalPlugins || [ ] ) ]
229+ const plugins = [ rawStreamPlugin , ...getSerovalPlugins ( ) ]
285230
286231 const refs = new Map ( )
287232 result = await processFramedResponse ( {
@@ -299,7 +244,7 @@ async function getResponse(fn: () => Promise<Response>) {
299244 const postProcessPromises : Array < Promise < unknown > > = [ ]
300245 setPostProcessContext ( postProcessPromises )
301246 try {
302- result = fromCrossJSON ( jsonPayload , { plugins : serovalPlugins ! } )
247+ result = fromCrossJSON ( jsonPayload , { plugins : getSerovalPlugins ( ) } )
303248 } finally {
304249 setPostProcessContext ( null )
305250 }
0 commit comments