Skip to content

Commit c7eddee

Browse files
committed
checkpoint
1 parent ed54879 commit c7eddee

File tree

7 files changed

+204
-132
lines changed

7 files changed

+204
-132
lines changed

e2e/react-start/server-functions/src/routes/submit-post-formdata.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ function SubmitPostFormDataFn() {
3333
<div className="p-2 m-2 grid gap-2">
3434
<h3>Submit POST FormData Fn Call</h3>
3535
<div className="overflow-y-auto">
36-
It should return navigate and return{' '}
36+
It should navigate to a raw response of {''}
3737
<code>
3838
<pre data-testid="expected-submit-post-formdata-server-fn-result">
3939
Hello, {testValues.name}!

packages/start-client-core/src/client-rpc/serverFnFetcher.ts

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,16 @@ import type { Plugin as SerovalPlugin } from 'seroval'
1717

1818
let serovalPlugins: Array<SerovalPlugin<any, any>> | null = null
1919

20+
// caller =>
21+
// serverFnFetcher =>
22+
// client =>
23+
// server =>
24+
// fn =>
25+
// seroval =>
26+
// client middleware =>
27+
// serverFnFetcher =>
28+
// caller
29+
2030
export async function serverFnFetcher(
2131
url: string,
2232
args: Array<any>,
@@ -37,7 +47,8 @@ export async function serverFnFetcher(
3747

3848
// Arrange the headers
3949
const headers = new Headers({
40-
'x-tsr-redirect': 'manual',
50+
'x-tsr-serverFn': 'true',
51+
'x-tsr-createServerFn': 'true',
4152
...(first.headers instanceof Headers
4253
? Object.fromEntries(first.headers.entries())
4354
: first.headers),
@@ -65,12 +76,6 @@ export async function serverFnFetcher(
6576
}
6677
}
6778

68-
if (url.includes('?')) {
69-
url += `&createServerFn`
70-
} else {
71-
url += `?createServerFn`
72-
}
73-
7479
let body = undefined
7580
if (first.method === 'POST') {
7681
const fetchBody = await getFetchBody(first)
@@ -97,6 +102,7 @@ export async function serverFnFetcher(
97102
handler(url, {
98103
method: 'POST',
99104
headers: {
105+
'x-tsr-serverFn': 'true',
100106
Accept: 'application/json',
101107
'Content-Type': 'application/json',
102108
},
@@ -165,7 +171,7 @@ async function getFetchBody(
165171
async function getResponse(fn: () => Promise<Response>) {
166172
const response = await (async () => {
167173
try {
168-
return await fn()
174+
return await fn() // client => server => fn => server => client
169175
} catch (error) {
170176
if (error instanceof Response) {
171177
return error
@@ -178,22 +184,16 @@ async function getResponse(fn: () => Promise<Response>) {
178184
if (response.headers.get(X_TSS_RAW_RESPONSE) === 'true') {
179185
return response
180186
}
187+
181188
const contentType = response.headers.get('content-type')
182189
invariant(contentType, 'expected content-type header to be set')
183190
const serializedByStart = !!response.headers.get(X_TSS_SERIALIZED)
184-
// If the response is not ok, throw an error
185-
if (!response.ok) {
186-
if (serializedByStart && contentType.includes('application/json')) {
187-
const jsonPayload = await response.json()
188-
const result = fromCrossJSON(jsonPayload, { plugins: serovalPlugins! })
189-
throw result
190-
}
191-
192-
throw new Error(await response.text())
193-
}
194191

192+
// If the response is serialized by the start server, we need to process it
193+
// differently than a normal response.
195194
if (serializedByStart) {
196195
let result
196+
// If it's a stream from the start serializer, process it as such
197197
if (contentType.includes('application/x-ndjson')) {
198198
const refs = new Map()
199199
result = await processServerFnResponse({
@@ -206,17 +206,22 @@ async function getResponse(fn: () => Promise<Response>) {
206206
},
207207
})
208208
}
209+
// If it's a JSON response, it can be simpler
209210
if (contentType.includes('application/json')) {
210211
const jsonPayload = await response.json()
211212
result = fromCrossJSON(jsonPayload, { plugins: serovalPlugins! })
212213
}
214+
213215
invariant(result, 'expected result to be resolved')
214216
if (result instanceof Error) {
215217
throw result
216218
}
219+
217220
return result
218221
}
219222

223+
// If it wasn't processed by the start serializer, check
224+
// if it's JSON
220225
if (contentType.includes('application/json')) {
221226
const jsonPayload = await response.json()
222227
const redirect = parseRedirect(jsonPayload)
@@ -229,6 +234,12 @@ async function getResponse(fn: () => Promise<Response>) {
229234
return jsonPayload
230235
}
231236

237+
// Othwerwise, if it's not OK, throw the content
238+
if (!response.ok) {
239+
throw new Error(await response.text())
240+
}
241+
242+
// Or return the response itself
232243
return response
233244
}
234245

packages/start-client-core/src/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ export const TSS_SERVER_FUNCTION_FACTORY = Symbol.for(
66

77
export const X_TSS_SERIALIZED = 'x-tss-serialized'
88
export const X_TSS_RAW_RESPONSE = 'x-tss-raw'
9+
export const X_TSS_CONTEXT = 'x-tss-context'
910
export {}

packages/start-client-core/src/createServerFn.ts

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { mergeHeaders } from '@tanstack/router-core/ssr/client'
22

3+
import { isRedirect, parseRedirect } from '@tanstack/router-core'
34
import { TSS_SERVER_FUNCTION_FACTORY } from './constants'
45
import { getStartOptions } from './getStartOptions'
56
import { getStartContextServerOnly } from './getStartContextServerOnly'
6-
import type { TSS_SERVER_FUNCTION } from './constants'
77
import type {
88
AnyValidator,
99
Constrain,
@@ -15,6 +15,7 @@ import type {
1515
ValidateSerializableInput,
1616
Validator,
1717
} from '@tanstack/router-core'
18+
import type { TSS_SERVER_FUNCTION } from './constants'
1819
import type {
1920
AnyFunctionMiddleware,
2021
AnyRequestMiddleware,
@@ -120,6 +121,11 @@ export const createServerFn: CreateServerFn<Register> = (options, __opts) => {
120121
context: {},
121122
})
122123

124+
const redirect = parseRedirect(result.error)
125+
if (redirect) {
126+
throw redirect
127+
}
128+
123129
if (result.error) throw result.error
124130
return result.result
125131
},
@@ -143,14 +149,18 @@ export const createServerFn: CreateServerFn<Register> = (options, __opts) => {
143149
request: startContext.request,
144150
}
145151

146-
return executeMiddleware(resolvedMiddleware, 'server', ctx).then(
147-
(d) => ({
148-
// Only send the result and sendContext back to the client
149-
result: d.result,
150-
error: d.error,
151-
context: d.sendContext,
152-
}),
153-
)
152+
const result = await executeMiddleware(
153+
resolvedMiddleware,
154+
'server',
155+
ctx,
156+
).then((d) => ({
157+
// Only send the result and sendContext back to the client
158+
result: d.result,
159+
error: d.error,
160+
context: d.sendContext,
161+
}))
162+
163+
return result
154164
},
155165
},
156166
) as any
@@ -257,6 +267,22 @@ export async function executeMiddleware(
257267
next: userNext as any,
258268
} as any)
259269

270+
// If result is NOT a ctx object, we need to return it as
271+
// the { result }
272+
if (isRedirect(result)) {
273+
return {
274+
...ctx,
275+
error: result,
276+
}
277+
}
278+
279+
if (result instanceof Response) {
280+
return {
281+
...ctx,
282+
result,
283+
}
284+
}
285+
260286
if (!(result as any)) {
261287
throw new Error(
262288
'User middleware returned undefined. You must call next() or return a result in your middlewares.',

packages/start-client-core/src/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ export {
8484
TSS_SERVER_FUNCTION,
8585
X_TSS_SERIALIZED,
8686
X_TSS_RAW_RESPONSE,
87+
X_TSS_CONTEXT,
8788
} from './constants'
8889

8990
export type * from './serverRoute'

packages/start-server-core/src/createStartHandler.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -268,16 +268,17 @@ export function createStartHandler<TRegister = Register>(
268268
[...middlewares, requestHandlerMiddleware],
269269
{
270270
request,
271-
272271
context: requestOpts?.context || {},
273272
},
274273
)
275274

276275
const response: Response = ctx.response
277276

277+
console.log('response', response)
278+
278279
if (isRedirect(response)) {
279280
if (isResolvedRedirect(response)) {
280-
if (request.headers.get('x-tsr-redirect') === 'manual') {
281+
if (request.headers.get('x-tsr-createServerFn') === 'true') {
281282
return json(
282283
{
283284
...response.options,
@@ -318,7 +319,7 @@ export function createStartHandler<TRegister = Register>(
318319
const router = await getRouter()
319320
const redirect = router.resolveRedirect(response)
320321

321-
if (request.headers.get('x-tsr-redirect') === 'manual') {
322+
if (request.headers.get('x-tsr-createServerFn') === 'true') {
322323
return json(
323324
{
324325
...response.options,

0 commit comments

Comments
 (0)