-
-
Notifications
You must be signed in to change notification settings - Fork 691
/
createServerFn.ts
97 lines (82 loc) · 2.32 KB
/
createServerFn.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import invariant from 'tiny-invariant'
export interface JsonResponse<TData> extends Response {
json: () => Promise<TData>
}
export type FetcherOptionsBase = {
method?: 'GET' | 'POST'
}
export type FetcherOptions = FetcherOptionsBase & {
requestInit?: RequestInit
}
export type FetchFnCtx = {
method: 'GET' | 'POST'
request: Request
}
export type FetchFn<TPayload, TResponse> = {
(payload: TPayload, ctx: FetchFnCtx): Promise<TResponse> | TResponse
url?: string
}
export type CompiledFetcherFnOptions<TPayload> = {
method: 'GET' | 'POST'
payload: TPayload | undefined
requestInit?: RequestInit
}
export type CompiledFetcherFn<TPayload, TResponse> = {
(opts: CompiledFetcherFnOptions<TPayload>): Promise<TResponse>
url: string
}
type IsPayloadOptional<T> = [T] extends [undefined] ? true : false
export type Fetcher<TPayload, TResponse> =
(IsPayloadOptional<TPayload> extends true
? (
payload?: TPayload,
opts?: FetcherOptions,
) => Promise<FetcherPayload<TResponse>>
: (
payload: TPayload,
opts?: FetcherOptions,
) => Promise<FetcherPayload<TResponse>>) & {
url: string
}
export type FetcherPayload<TResponse> = WrapRSCs<
TResponse extends JsonResponse<infer TData> ? TData : TResponse
>
type WrapRSCs<T> = T extends JSX.Element
? ReadableStream
: T extends Record<string, any>
? {
[K in keyof T]: WrapRSCs<T[K]>
}
: T extends Array<infer U>
? Array<WrapRSCs<U>>
: T
export type RscStream<T> = {
__cacheState: T
}
export function createServerFn<
TMethod extends 'GET' | 'POST',
TPayload = undefined,
TResponse = unknown,
>(
method: TMethod,
fn: FetchFn<TPayload, TResponse>,
): Fetcher<TPayload, TResponse> {
// Cast the compiled function that will be injected by vinxi
const compiledFn = fn as unknown as CompiledFetcherFn<TPayload, TResponse>
invariant(
compiledFn.url,
`createServerFn must be called with a function that is marked with the 'use server' pragma. Are you using the @tanstack/router-plugin/vite ?`,
)
return Object.assign(
async (payload: TPayload, opts?: FetcherOptions) => {
return compiledFn({
method,
payload: payload || undefined,
requestInit: opts?.requestInit,
})
},
{
url: fn.url!,
},
) as Fetcher<TPayload, TResponse>
}