@benjavicente/start-client-core 1.167.9
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/LICENSE +21 -0
- package/README.md +12 -0
- package/bin/intent.js +25 -0
- package/dist/esm/client/ServerFunctionSerializationAdapter.d.ts +7 -0
- package/dist/esm/client/ServerFunctionSerializationAdapter.js +18 -0
- package/dist/esm/client/ServerFunctionSerializationAdapter.js.map +1 -0
- package/dist/esm/client/hydrateStart.d.ts +2 -0
- package/dist/esm/client/hydrateStart.js +31 -0
- package/dist/esm/client/hydrateStart.js.map +1 -0
- package/dist/esm/client/index.d.ts +2 -0
- package/dist/esm/client/index.js +2 -0
- package/dist/esm/client-rpc/createClientRpc.d.ts +6 -0
- package/dist/esm/client-rpc/createClientRpc.js +21 -0
- package/dist/esm/client-rpc/createClientRpc.js.map +1 -0
- package/dist/esm/client-rpc/frame-decoder.d.ts +23 -0
- package/dist/esm/client-rpc/frame-decoder.js +231 -0
- package/dist/esm/client-rpc/frame-decoder.js.map +1 -0
- package/dist/esm/client-rpc/index.d.ts +1 -0
- package/dist/esm/client-rpc/index.js +2 -0
- package/dist/esm/client-rpc/serverFnFetcher.d.ts +1 -0
- package/dist/esm/client-rpc/serverFnFetcher.js +231 -0
- package/dist/esm/client-rpc/serverFnFetcher.js.map +1 -0
- package/dist/esm/constants.d.ts +53 -0
- package/dist/esm/constants.js +46 -0
- package/dist/esm/constants.js.map +1 -0
- package/dist/esm/createMiddleware.d.ts +195 -0
- package/dist/esm/createMiddleware.js +26 -0
- package/dist/esm/createMiddleware.js.map +1 -0
- package/dist/esm/createServerFn.d.ts +131 -0
- package/dist/esm/createServerFn.js +200 -0
- package/dist/esm/createServerFn.js.map +1 -0
- package/dist/esm/createStart.d.ts +50 -0
- package/dist/esm/createStart.js +29 -0
- package/dist/esm/createStart.js.map +1 -0
- package/dist/esm/fake-start-entry.d.ts +2 -0
- package/dist/esm/fake-start-entry.js +7 -0
- package/dist/esm/fake-start-entry.js.map +1 -0
- package/dist/esm/getDefaultSerovalPlugins.d.ts +1 -0
- package/dist/esm/getDefaultSerovalPlugins.js +10 -0
- package/dist/esm/getDefaultSerovalPlugins.js.map +1 -0
- package/dist/esm/getGlobalStartContext.d.ts +3 -0
- package/dist/esm/getGlobalStartContext.js +12 -0
- package/dist/esm/getGlobalStartContext.js.map +1 -0
- package/dist/esm/getRouterInstance.d.ts +2 -0
- package/dist/esm/getRouterInstance.js +8 -0
- package/dist/esm/getRouterInstance.js.map +1 -0
- package/dist/esm/getStartContextServerOnly.d.ts +2 -0
- package/dist/esm/getStartContextServerOnly.js +8 -0
- package/dist/esm/getStartContextServerOnly.js.map +1 -0
- package/dist/esm/getStartOptions.d.ts +2 -0
- package/dist/esm/getStartOptions.js +8 -0
- package/dist/esm/getStartOptions.js.map +1 -0
- package/dist/esm/global.d.ts +7 -0
- package/dist/esm/index.d.ts +20 -0
- package/dist/esm/index.js +12 -0
- package/dist/esm/safeObjectMerge.d.ts +10 -0
- package/dist/esm/safeObjectMerge.js +30 -0
- package/dist/esm/safeObjectMerge.js.map +1 -0
- package/dist/esm/serverRoute.d.ts +65 -0
- package/dist/esm/startEntry.d.ts +8 -0
- package/dist/esm/tests/createServerFn.test-d.d.ts +1 -0
- package/dist/esm/tests/createServerMiddleware.test-d.d.ts +1 -0
- package/package.json +98 -0
- package/skills/start-core/SKILL.md +210 -0
- package/skills/start-core/deployment/SKILL.md +306 -0
- package/skills/start-core/execution-model/SKILL.md +302 -0
- package/skills/start-core/middleware/SKILL.md +365 -0
- package/skills/start-core/server-functions/SKILL.md +335 -0
- package/skills/start-core/server-routes/SKILL.md +280 -0
- package/src/client/ServerFunctionSerializationAdapter.ts +16 -0
- package/src/client/hydrateStart.ts +43 -0
- package/src/client/index.ts +2 -0
- package/src/client-rpc/createClientRpc.ts +20 -0
- package/src/client-rpc/frame-decoder.ts +389 -0
- package/src/client-rpc/index.ts +1 -0
- package/src/client-rpc/serverFnFetcher.ts +416 -0
- package/src/constants.ts +90 -0
- package/src/createMiddleware.ts +824 -0
- package/src/createServerFn.ts +813 -0
- package/src/createStart.ts +166 -0
- package/src/fake-start-entry.ts +2 -0
- package/src/getDefaultSerovalPlugins.ts +17 -0
- package/src/getGlobalStartContext.ts +18 -0
- package/src/getRouterInstance.ts +8 -0
- package/src/getStartContextServerOnly.ts +4 -0
- package/src/getStartOptions.ts +8 -0
- package/src/global.ts +9 -0
- package/src/index.tsx +119 -0
- package/src/safeObjectMerge.ts +38 -0
- package/src/serverRoute.ts +509 -0
- package/src/start-entry.d.ts +11 -0
- package/src/startEntry.ts +10 -0
- package/src/tests/createServerFn.test-d.ts +866 -0
- package/src/tests/createServerMiddleware.test-d.ts +810 -0
|
@@ -0,0 +1,866 @@
|
|
|
1
|
+
import { describe, expectTypeOf, test } from 'vitest'
|
|
2
|
+
import { createMiddleware } from '../createMiddleware'
|
|
3
|
+
import { createServerFn } from '../createServerFn'
|
|
4
|
+
import { TSS_SERVER_FUNCTION } from '../constants'
|
|
5
|
+
import type { ServerFnMeta } from '../constants'
|
|
6
|
+
import type {
|
|
7
|
+
Constrain,
|
|
8
|
+
Register,
|
|
9
|
+
TsrSerializable,
|
|
10
|
+
ValidateSerializableInput,
|
|
11
|
+
Validator,
|
|
12
|
+
} from '@benjavicente/router-core'
|
|
13
|
+
import type {
|
|
14
|
+
ConstrainValidator,
|
|
15
|
+
CustomFetch,
|
|
16
|
+
ServerFnReturnType,
|
|
17
|
+
} from '../createServerFn'
|
|
18
|
+
|
|
19
|
+
test('createServerFn without middleware', () => {
|
|
20
|
+
expectTypeOf(createServerFn()).toHaveProperty('handler')
|
|
21
|
+
expectTypeOf(createServerFn()).toHaveProperty('middleware')
|
|
22
|
+
expectTypeOf(createServerFn()).toHaveProperty('inputValidator')
|
|
23
|
+
|
|
24
|
+
createServerFn({ method: 'GET' }).handler((options) => {
|
|
25
|
+
expectTypeOf(options).toEqualTypeOf<{
|
|
26
|
+
context: undefined
|
|
27
|
+
data: undefined
|
|
28
|
+
method: 'GET'
|
|
29
|
+
serverFnMeta: ServerFnMeta
|
|
30
|
+
}>()
|
|
31
|
+
})
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
test('createServerFn with validator function', () => {
|
|
35
|
+
const fnAfterValidator = createServerFn({
|
|
36
|
+
method: 'GET',
|
|
37
|
+
}).inputValidator((input: { input: string }) => ({
|
|
38
|
+
a: input.input,
|
|
39
|
+
}))
|
|
40
|
+
|
|
41
|
+
expectTypeOf(fnAfterValidator).toHaveProperty('handler')
|
|
42
|
+
expectTypeOf(fnAfterValidator).toHaveProperty('middleware')
|
|
43
|
+
expectTypeOf(fnAfterValidator).not.toHaveProperty('inputValidator')
|
|
44
|
+
|
|
45
|
+
const fn = fnAfterValidator.handler((options) => {
|
|
46
|
+
expectTypeOf(options).toEqualTypeOf<{
|
|
47
|
+
context: undefined
|
|
48
|
+
data: {
|
|
49
|
+
a: string
|
|
50
|
+
}
|
|
51
|
+
method: 'GET'
|
|
52
|
+
serverFnMeta: ServerFnMeta
|
|
53
|
+
}>()
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
expectTypeOf(fn).parameter(0).toEqualTypeOf<{
|
|
57
|
+
data: { input: string }
|
|
58
|
+
headers?: HeadersInit
|
|
59
|
+
signal?: AbortSignal
|
|
60
|
+
fetch?: CustomFetch
|
|
61
|
+
}>()
|
|
62
|
+
|
|
63
|
+
expectTypeOf<ReturnType<typeof fn>>().resolves.toEqualTypeOf<void>()
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
test('createServerFn with async validator function', () => {
|
|
67
|
+
const fnAfterValidator = createServerFn({
|
|
68
|
+
method: 'GET',
|
|
69
|
+
}).inputValidator((input: string) => Promise.resolve(input))
|
|
70
|
+
|
|
71
|
+
expectTypeOf(fnAfterValidator).toHaveProperty('handler')
|
|
72
|
+
expectTypeOf(fnAfterValidator).toHaveProperty('middleware')
|
|
73
|
+
expectTypeOf(fnAfterValidator).not.toHaveProperty('inputValidator')
|
|
74
|
+
|
|
75
|
+
const fn = fnAfterValidator.handler((options) => {
|
|
76
|
+
expectTypeOf(options).toEqualTypeOf<{
|
|
77
|
+
context: undefined
|
|
78
|
+
data: string
|
|
79
|
+
method: 'GET'
|
|
80
|
+
serverFnMeta: ServerFnMeta
|
|
81
|
+
}>()
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
expectTypeOf(fn).parameter(0).toEqualTypeOf<{
|
|
85
|
+
data: string
|
|
86
|
+
headers?: HeadersInit
|
|
87
|
+
signal?: AbortSignal
|
|
88
|
+
fetch?: CustomFetch
|
|
89
|
+
}>()
|
|
90
|
+
|
|
91
|
+
expectTypeOf<ReturnType<typeof fn>>().resolves.toEqualTypeOf<void>()
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
test('createServerFn with validator with parse method', () => {
|
|
95
|
+
const fnAfterValidator = createServerFn({
|
|
96
|
+
method: 'GET',
|
|
97
|
+
}).inputValidator({
|
|
98
|
+
parse: (input: string) => input,
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
expectTypeOf(fnAfterValidator).toHaveProperty('handler')
|
|
102
|
+
expectTypeOf(fnAfterValidator).toHaveProperty('middleware')
|
|
103
|
+
expectTypeOf(fnAfterValidator).not.toHaveProperty('inputValidator')
|
|
104
|
+
|
|
105
|
+
const fn = fnAfterValidator.handler((options) => {
|
|
106
|
+
expectTypeOf(options).toEqualTypeOf<{
|
|
107
|
+
context: undefined
|
|
108
|
+
data: string
|
|
109
|
+
method: 'GET'
|
|
110
|
+
serverFnMeta: ServerFnMeta
|
|
111
|
+
}>()
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
expectTypeOf(fn).parameter(0).toEqualTypeOf<{
|
|
115
|
+
data: string
|
|
116
|
+
headers?: HeadersInit
|
|
117
|
+
signal?: AbortSignal
|
|
118
|
+
fetch?: CustomFetch
|
|
119
|
+
}>()
|
|
120
|
+
|
|
121
|
+
expectTypeOf<ReturnType<typeof fn>>().resolves.toEqualTypeOf<void>()
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
test('createServerFn with async validator with parse method', () => {
|
|
125
|
+
const fnAfterValidator = createServerFn({
|
|
126
|
+
method: 'GET',
|
|
127
|
+
}).inputValidator({
|
|
128
|
+
parse: (input: string) => Promise.resolve(input),
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
expectTypeOf(fnAfterValidator).toHaveProperty('handler')
|
|
132
|
+
expectTypeOf(fnAfterValidator).toHaveProperty('middleware')
|
|
133
|
+
expectTypeOf(fnAfterValidator).not.toHaveProperty('inputValidator')
|
|
134
|
+
|
|
135
|
+
const fn = fnAfterValidator.handler((options) => {
|
|
136
|
+
expectTypeOf(options).toEqualTypeOf<{
|
|
137
|
+
context: undefined
|
|
138
|
+
data: string
|
|
139
|
+
method: 'GET'
|
|
140
|
+
serverFnMeta: ServerFnMeta
|
|
141
|
+
}>()
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
expectTypeOf(fn).parameter(0).toEqualTypeOf<{
|
|
145
|
+
data: string
|
|
146
|
+
headers?: HeadersInit
|
|
147
|
+
signal?: AbortSignal
|
|
148
|
+
fetch?: CustomFetch
|
|
149
|
+
}>()
|
|
150
|
+
|
|
151
|
+
expectTypeOf<ReturnType<typeof fn>>().resolves.toEqualTypeOf<void>()
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
test('createServerFn with standard validator', () => {
|
|
155
|
+
interface SyncValidator {
|
|
156
|
+
readonly '~standard': {
|
|
157
|
+
types?: {
|
|
158
|
+
input: string
|
|
159
|
+
output: string
|
|
160
|
+
}
|
|
161
|
+
validate: (input: unknown) => {
|
|
162
|
+
value: string
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
const validator: SyncValidator = {
|
|
167
|
+
['~standard']: {
|
|
168
|
+
validate: (input: unknown) => ({
|
|
169
|
+
value: input as string,
|
|
170
|
+
}),
|
|
171
|
+
},
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const fnAfterValidator = createServerFn({
|
|
175
|
+
method: 'GET',
|
|
176
|
+
}).inputValidator(validator)
|
|
177
|
+
|
|
178
|
+
expectTypeOf(fnAfterValidator).toHaveProperty('handler')
|
|
179
|
+
expectTypeOf(fnAfterValidator).toHaveProperty('middleware')
|
|
180
|
+
expectTypeOf(fnAfterValidator).not.toHaveProperty('inputValidator')
|
|
181
|
+
|
|
182
|
+
const fn = fnAfterValidator.handler((options) => {
|
|
183
|
+
expectTypeOf(options).toEqualTypeOf<{
|
|
184
|
+
context: undefined
|
|
185
|
+
data: string
|
|
186
|
+
method: 'GET'
|
|
187
|
+
serverFnMeta: ServerFnMeta
|
|
188
|
+
}>()
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
expectTypeOf(fn).parameter(0).toEqualTypeOf<{
|
|
192
|
+
data: string
|
|
193
|
+
headers?: HeadersInit
|
|
194
|
+
signal?: AbortSignal
|
|
195
|
+
fetch?: CustomFetch
|
|
196
|
+
}>()
|
|
197
|
+
|
|
198
|
+
expectTypeOf<ReturnType<typeof fn>>().resolves.toEqualTypeOf<void>()
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
test('createServerFn with async standard validator', () => {
|
|
202
|
+
interface AsyncValidator {
|
|
203
|
+
readonly '~standard': {
|
|
204
|
+
types?: {
|
|
205
|
+
input: string
|
|
206
|
+
output: string
|
|
207
|
+
}
|
|
208
|
+
validate: (input: unknown) => Promise<{
|
|
209
|
+
value: string
|
|
210
|
+
}>
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
const validator: AsyncValidator = {
|
|
214
|
+
['~standard']: {
|
|
215
|
+
validate: (input: unknown) =>
|
|
216
|
+
Promise.resolve({
|
|
217
|
+
value: input as string,
|
|
218
|
+
}),
|
|
219
|
+
},
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const fnAfterValidator = createServerFn({
|
|
223
|
+
method: 'GET',
|
|
224
|
+
}).inputValidator(validator)
|
|
225
|
+
|
|
226
|
+
expectTypeOf(fnAfterValidator).toHaveProperty('handler')
|
|
227
|
+
expectTypeOf(fnAfterValidator).toHaveProperty('middleware')
|
|
228
|
+
expectTypeOf(fnAfterValidator).not.toHaveProperty('inputValidator')
|
|
229
|
+
|
|
230
|
+
const fn = fnAfterValidator.handler((options) => {
|
|
231
|
+
expectTypeOf(options).toEqualTypeOf<{
|
|
232
|
+
context: undefined
|
|
233
|
+
data: string
|
|
234
|
+
method: 'GET'
|
|
235
|
+
serverFnMeta: ServerFnMeta
|
|
236
|
+
}>()
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
expectTypeOf(fn).parameter(0).toEqualTypeOf<{
|
|
240
|
+
data: string
|
|
241
|
+
headers?: HeadersInit
|
|
242
|
+
signal?: AbortSignal
|
|
243
|
+
fetch?: CustomFetch
|
|
244
|
+
}>()
|
|
245
|
+
|
|
246
|
+
expectTypeOf<ReturnType<typeof fn>>().resolves.toEqualTypeOf<void>()
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
test('createServerFn with middleware and context', () => {
|
|
250
|
+
const middleware1 = createMiddleware({ type: 'function' }).server(
|
|
251
|
+
({ next }) => {
|
|
252
|
+
return next({ context: { a: 'a' } as const })
|
|
253
|
+
},
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
const middleware2 = createMiddleware({ type: 'function' }).server(
|
|
257
|
+
({ next }) => {
|
|
258
|
+
return next({ context: { b: 'b' } as const })
|
|
259
|
+
},
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
const middleware3 = createMiddleware({ type: 'function' })
|
|
263
|
+
.middleware([middleware1, middleware2])
|
|
264
|
+
.client(({ next }) => {
|
|
265
|
+
return next({ context: { c: 'c' } as const })
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
const middleware4 = createMiddleware({ type: 'function' })
|
|
269
|
+
.middleware([middleware3])
|
|
270
|
+
.client(({ context, next }) => {
|
|
271
|
+
return next({ sendContext: context })
|
|
272
|
+
})
|
|
273
|
+
.server(({ context, next }) => {
|
|
274
|
+
expectTypeOf(context).toEqualTypeOf<{
|
|
275
|
+
readonly a: 'a'
|
|
276
|
+
readonly b: 'b'
|
|
277
|
+
readonly c: 'c'
|
|
278
|
+
}>()
|
|
279
|
+
return next({ context: { d: 'd' } as const })
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
const fnWithMiddleware = createServerFn({ method: 'GET' }).middleware([
|
|
283
|
+
middleware4,
|
|
284
|
+
])
|
|
285
|
+
|
|
286
|
+
expectTypeOf(fnWithMiddleware).toHaveProperty('handler')
|
|
287
|
+
expectTypeOf(fnWithMiddleware).toHaveProperty('inputValidator')
|
|
288
|
+
|
|
289
|
+
fnWithMiddleware.handler((options) => {
|
|
290
|
+
expectTypeOf(options).toEqualTypeOf<{
|
|
291
|
+
context: {
|
|
292
|
+
readonly a: 'a'
|
|
293
|
+
readonly b: 'b'
|
|
294
|
+
readonly c: 'c'
|
|
295
|
+
readonly d: 'd'
|
|
296
|
+
}
|
|
297
|
+
data: undefined
|
|
298
|
+
method: 'GET'
|
|
299
|
+
serverFnMeta: ServerFnMeta
|
|
300
|
+
}>()
|
|
301
|
+
})
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
describe('createServerFn with middleware and validator', () => {
|
|
305
|
+
const middleware1 = createMiddleware({ type: 'function' }).inputValidator(
|
|
306
|
+
(input: { readonly inputA: 'inputA' }) =>
|
|
307
|
+
({
|
|
308
|
+
outputA: 'outputA',
|
|
309
|
+
}) as const,
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
const middleware2 = createMiddleware({ type: 'function' }).inputValidator(
|
|
313
|
+
(input: { readonly inputB: 'inputB' }) =>
|
|
314
|
+
({
|
|
315
|
+
outputB: 'outputB',
|
|
316
|
+
}) as const,
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
const middleware3 = createMiddleware({ type: 'function' }).middleware([
|
|
320
|
+
middleware1,
|
|
321
|
+
middleware2,
|
|
322
|
+
])
|
|
323
|
+
|
|
324
|
+
test(`response`, () => {
|
|
325
|
+
const fn = createServerFn({ method: 'GET' })
|
|
326
|
+
.middleware([middleware3])
|
|
327
|
+
.inputValidator(
|
|
328
|
+
(input: { readonly inputC: 'inputC' }) =>
|
|
329
|
+
({
|
|
330
|
+
outputC: 'outputC',
|
|
331
|
+
}) as const,
|
|
332
|
+
)
|
|
333
|
+
.handler((options) => {
|
|
334
|
+
expectTypeOf(options).toEqualTypeOf<{
|
|
335
|
+
context: undefined
|
|
336
|
+
data: {
|
|
337
|
+
readonly outputA: 'outputA'
|
|
338
|
+
readonly outputB: 'outputB'
|
|
339
|
+
readonly outputC: 'outputC'
|
|
340
|
+
}
|
|
341
|
+
method: 'GET'
|
|
342
|
+
serverFnMeta: ServerFnMeta
|
|
343
|
+
}>()
|
|
344
|
+
|
|
345
|
+
return 'some-data' as const
|
|
346
|
+
})
|
|
347
|
+
|
|
348
|
+
expectTypeOf(fn).parameter(0).toEqualTypeOf<{
|
|
349
|
+
data: {
|
|
350
|
+
readonly inputA: 'inputA'
|
|
351
|
+
readonly inputB: 'inputB'
|
|
352
|
+
readonly inputC: 'inputC'
|
|
353
|
+
}
|
|
354
|
+
headers?: HeadersInit
|
|
355
|
+
signal?: AbortSignal
|
|
356
|
+
fetch?: CustomFetch
|
|
357
|
+
}>()
|
|
358
|
+
|
|
359
|
+
expectTypeOf(fn).returns.resolves.toEqualTypeOf<'some-data'>()
|
|
360
|
+
expectTypeOf(() =>
|
|
361
|
+
fn({
|
|
362
|
+
data: { inputA: 'inputA', inputB: 'inputB', inputC: 'inputC' },
|
|
363
|
+
}),
|
|
364
|
+
).returns.resolves.toEqualTypeOf<'some-data'>()
|
|
365
|
+
})
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
test('createServerFn overrides properties', () => {
|
|
369
|
+
const middleware1 = createMiddleware({ type: 'function' })
|
|
370
|
+
.inputValidator(
|
|
371
|
+
() =>
|
|
372
|
+
({
|
|
373
|
+
input: 'a' as 'a' | 'b' | 'c',
|
|
374
|
+
}) as const,
|
|
375
|
+
)
|
|
376
|
+
.client(({ context, next }) => {
|
|
377
|
+
expectTypeOf(context).toEqualTypeOf<undefined>()
|
|
378
|
+
|
|
379
|
+
const newContext = { context: 'a' } as const
|
|
380
|
+
return next({ sendContext: newContext, context: newContext })
|
|
381
|
+
})
|
|
382
|
+
.server(({ data, context, next }) => {
|
|
383
|
+
expectTypeOf(data).toEqualTypeOf<{ readonly input: 'a' | 'b' | 'c' }>()
|
|
384
|
+
|
|
385
|
+
expectTypeOf(context).toEqualTypeOf<{
|
|
386
|
+
readonly context: 'a'
|
|
387
|
+
}>()
|
|
388
|
+
|
|
389
|
+
const newContext = { context: 'b' } as const
|
|
390
|
+
return next({ sendContext: newContext, context: newContext })
|
|
391
|
+
})
|
|
392
|
+
|
|
393
|
+
const middleware2 = createMiddleware({ type: 'function' })
|
|
394
|
+
.middleware([middleware1])
|
|
395
|
+
.inputValidator(
|
|
396
|
+
() =>
|
|
397
|
+
({
|
|
398
|
+
input: 'b' as 'b' | 'c',
|
|
399
|
+
}) as const,
|
|
400
|
+
)
|
|
401
|
+
.client(({ context, next }) => {
|
|
402
|
+
expectTypeOf(context).toEqualTypeOf<{ readonly context: 'a' }>()
|
|
403
|
+
|
|
404
|
+
const newContext = { context: 'aa' } as const
|
|
405
|
+
|
|
406
|
+
return next({ sendContext: newContext, context: newContext })
|
|
407
|
+
})
|
|
408
|
+
.server(({ context, next }) => {
|
|
409
|
+
expectTypeOf(context).toEqualTypeOf<{ readonly context: 'aa' }>()
|
|
410
|
+
|
|
411
|
+
const newContext = { context: 'bb' } as const
|
|
412
|
+
|
|
413
|
+
return next({ sendContext: newContext, context: newContext })
|
|
414
|
+
})
|
|
415
|
+
|
|
416
|
+
createServerFn()
|
|
417
|
+
.middleware([middleware2])
|
|
418
|
+
.inputValidator(
|
|
419
|
+
() =>
|
|
420
|
+
({
|
|
421
|
+
input: 'c',
|
|
422
|
+
}) as const,
|
|
423
|
+
)
|
|
424
|
+
.handler(({ data, context }) => {
|
|
425
|
+
expectTypeOf(data).toEqualTypeOf<{
|
|
426
|
+
readonly input: 'c'
|
|
427
|
+
}>()
|
|
428
|
+
expectTypeOf(context).toEqualTypeOf<{ readonly context: 'bb' }>()
|
|
429
|
+
})
|
|
430
|
+
})
|
|
431
|
+
|
|
432
|
+
test('createServerFn where validator is a primitive', () => {
|
|
433
|
+
createServerFn({ method: 'GET' })
|
|
434
|
+
.inputValidator(() => 'c' as const)
|
|
435
|
+
.handler((options) => {
|
|
436
|
+
expectTypeOf(options).toEqualTypeOf<{
|
|
437
|
+
context: undefined
|
|
438
|
+
data: 'c'
|
|
439
|
+
method: 'GET'
|
|
440
|
+
serverFnMeta: ServerFnMeta
|
|
441
|
+
}>()
|
|
442
|
+
})
|
|
443
|
+
})
|
|
444
|
+
|
|
445
|
+
test('createServerFn where validator is optional if object is optional', () => {
|
|
446
|
+
const fn = createServerFn({ method: 'GET' })
|
|
447
|
+
.inputValidator((input: 'c' | undefined) => input)
|
|
448
|
+
.handler((options) => {
|
|
449
|
+
expectTypeOf(options).toEqualTypeOf<{
|
|
450
|
+
context: undefined
|
|
451
|
+
data: 'c' | undefined
|
|
452
|
+
method: 'GET'
|
|
453
|
+
serverFnMeta: ServerFnMeta
|
|
454
|
+
}>()
|
|
455
|
+
})
|
|
456
|
+
|
|
457
|
+
expectTypeOf(fn).parameter(0).toEqualTypeOf<
|
|
458
|
+
| {
|
|
459
|
+
data?: 'c' | undefined
|
|
460
|
+
headers?: HeadersInit
|
|
461
|
+
signal?: AbortSignal
|
|
462
|
+
fetch?: CustomFetch
|
|
463
|
+
}
|
|
464
|
+
| undefined
|
|
465
|
+
>()
|
|
466
|
+
|
|
467
|
+
expectTypeOf<ReturnType<typeof fn>>().resolves.toEqualTypeOf<void>()
|
|
468
|
+
})
|
|
469
|
+
|
|
470
|
+
test('createServerFn where data is optional if there is no validator', () => {
|
|
471
|
+
const fn = createServerFn({ method: 'GET' }).handler((options) => {
|
|
472
|
+
expectTypeOf(options).toEqualTypeOf<{
|
|
473
|
+
context: undefined
|
|
474
|
+
data: undefined
|
|
475
|
+
method: 'GET'
|
|
476
|
+
serverFnMeta: ServerFnMeta
|
|
477
|
+
}>()
|
|
478
|
+
})
|
|
479
|
+
|
|
480
|
+
expectTypeOf(fn).parameter(0).toEqualTypeOf<
|
|
481
|
+
| {
|
|
482
|
+
data?: undefined
|
|
483
|
+
headers?: HeadersInit
|
|
484
|
+
signal?: AbortSignal
|
|
485
|
+
fetch?: CustomFetch
|
|
486
|
+
}
|
|
487
|
+
| undefined
|
|
488
|
+
>()
|
|
489
|
+
|
|
490
|
+
expectTypeOf<ReturnType<typeof fn>>().resolves.toEqualTypeOf<void>()
|
|
491
|
+
})
|
|
492
|
+
|
|
493
|
+
test('createServerFn returns Date', () => {
|
|
494
|
+
const fn = createServerFn().handler(() => ({
|
|
495
|
+
dates: [new Date(), new Date()] as const,
|
|
496
|
+
}))
|
|
497
|
+
|
|
498
|
+
expectTypeOf<ReturnType<typeof fn>>().toMatchTypeOf<Promise<unknown>>()
|
|
499
|
+
expectTypeOf<Awaited<ReturnType<typeof fn>>>().toMatchTypeOf<
|
|
500
|
+
ValidateSerializableInput<Register, { dates: readonly [Date, Date] }>
|
|
501
|
+
>()
|
|
502
|
+
})
|
|
503
|
+
|
|
504
|
+
test('createServerFn returns undefined', () => {
|
|
505
|
+
const fn = createServerFn().handler(() => ({
|
|
506
|
+
nothing: undefined,
|
|
507
|
+
}))
|
|
508
|
+
|
|
509
|
+
expectTypeOf(fn()).toEqualTypeOf<Promise<{ nothing: undefined }>>()
|
|
510
|
+
})
|
|
511
|
+
|
|
512
|
+
test('createServerFn cannot return function', () => {
|
|
513
|
+
expectTypeOf(createServerFn().handler<{ func: () => 'func' }>)
|
|
514
|
+
.parameter(0)
|
|
515
|
+
.returns.toEqualTypeOf<{ func: 'Function is not serializable' }>()
|
|
516
|
+
})
|
|
517
|
+
|
|
518
|
+
test('createServerFn cannot validate function', () => {
|
|
519
|
+
const validator = createServerFn().inputValidator<
|
|
520
|
+
(input: { func: () => 'string' }) => { output: 'string' }
|
|
521
|
+
>
|
|
522
|
+
|
|
523
|
+
expectTypeOf(validator)
|
|
524
|
+
.parameter(0)
|
|
525
|
+
.toEqualTypeOf<
|
|
526
|
+
Constrain<
|
|
527
|
+
(input: { func: () => 'string' }) => { output: 'string' },
|
|
528
|
+
Validator<{ func: 'Function is not serializable' }, any>
|
|
529
|
+
>
|
|
530
|
+
>()
|
|
531
|
+
})
|
|
532
|
+
|
|
533
|
+
test('createServerFn can validate Date', () => {
|
|
534
|
+
const validator = createServerFn().inputValidator<
|
|
535
|
+
(input: Date) => { output: 'string' }
|
|
536
|
+
>
|
|
537
|
+
|
|
538
|
+
expectTypeOf(validator)
|
|
539
|
+
.parameter(0)
|
|
540
|
+
.toEqualTypeOf<
|
|
541
|
+
ConstrainValidator<Register, 'GET', (input: Date) => { output: 'string' }>
|
|
542
|
+
>()
|
|
543
|
+
})
|
|
544
|
+
|
|
545
|
+
test('createServerFn can validate FormData', () => {
|
|
546
|
+
const validator = createServerFn({ method: 'POST' }).inputValidator<
|
|
547
|
+
(input: FormData) => { output: 'string' }
|
|
548
|
+
>
|
|
549
|
+
|
|
550
|
+
expectTypeOf(validator).parameter(0).parameter(0).toEqualTypeOf<FormData>()
|
|
551
|
+
})
|
|
552
|
+
|
|
553
|
+
test('createServerFn cannot validate FormData for GET', () => {
|
|
554
|
+
const validator = createServerFn({ method: 'GET' }).inputValidator<
|
|
555
|
+
(input: FormData) => { output: 'string' }
|
|
556
|
+
>
|
|
557
|
+
|
|
558
|
+
expectTypeOf(validator)
|
|
559
|
+
.parameter(0)
|
|
560
|
+
.parameter(0)
|
|
561
|
+
.not.toEqualTypeOf<FormData>()
|
|
562
|
+
})
|
|
563
|
+
|
|
564
|
+
describe('response', () => {
|
|
565
|
+
test(`client receives Response when Response is returned`, () => {
|
|
566
|
+
const fn = createServerFn().handler(() => {
|
|
567
|
+
return new Response('Hello World')
|
|
568
|
+
})
|
|
569
|
+
|
|
570
|
+
expectTypeOf(fn()).toEqualTypeOf<Promise<Response>>()
|
|
571
|
+
})
|
|
572
|
+
|
|
573
|
+
test(`client receives union when handler may return Response or string`, () => {
|
|
574
|
+
const fn = createServerFn().handler(() => {
|
|
575
|
+
const result: Response | 'Hello World' =
|
|
576
|
+
Math.random() > 0.5 ? new Response('Hello World') : 'Hello World'
|
|
577
|
+
|
|
578
|
+
return result
|
|
579
|
+
})
|
|
580
|
+
|
|
581
|
+
expectTypeOf(fn()).toEqualTypeOf<Promise<Response | 'Hello World'>>()
|
|
582
|
+
})
|
|
583
|
+
})
|
|
584
|
+
|
|
585
|
+
test('ServerFnReturnType distributes Response union', () => {
|
|
586
|
+
expectTypeOf<
|
|
587
|
+
ServerFnReturnType<Register, Response | 'Hello World'>
|
|
588
|
+
>().toEqualTypeOf<Response | 'Hello World'>()
|
|
589
|
+
})
|
|
590
|
+
|
|
591
|
+
test('createServerFn can be used as a mutation function', () => {
|
|
592
|
+
const serverFn = createServerFn()
|
|
593
|
+
.inputValidator((data: number) => data)
|
|
594
|
+
.handler(() => 'foo')
|
|
595
|
+
|
|
596
|
+
type MutationFunction<TData = unknown, TVariables = unknown> = (
|
|
597
|
+
variables: TVariables,
|
|
598
|
+
) => Promise<TData>
|
|
599
|
+
|
|
600
|
+
// simplifeid "clone" of @tansctack/react-query's useMutation
|
|
601
|
+
const useMutation = <TData, TVariables>(
|
|
602
|
+
fn: MutationFunction<TData, TVariables>,
|
|
603
|
+
) => {}
|
|
604
|
+
|
|
605
|
+
useMutation(serverFn)
|
|
606
|
+
})
|
|
607
|
+
|
|
608
|
+
test('createServerFn validator infers unknown for default input type', () => {
|
|
609
|
+
const fn = createServerFn()
|
|
610
|
+
.inputValidator((input) => {
|
|
611
|
+
expectTypeOf(input).toEqualTypeOf<unknown>()
|
|
612
|
+
|
|
613
|
+
if (typeof input === 'number') return 'success' as const
|
|
614
|
+
|
|
615
|
+
return 'failed' as const
|
|
616
|
+
})
|
|
617
|
+
.handler(({ data }) => {
|
|
618
|
+
expectTypeOf(data).toEqualTypeOf<'success' | 'failed'>()
|
|
619
|
+
|
|
620
|
+
return data
|
|
621
|
+
})
|
|
622
|
+
|
|
623
|
+
expectTypeOf(fn).parameter(0).toEqualTypeOf<
|
|
624
|
+
| {
|
|
625
|
+
data?: unknown | undefined
|
|
626
|
+
headers?: HeadersInit
|
|
627
|
+
signal?: AbortSignal
|
|
628
|
+
fetch?: CustomFetch
|
|
629
|
+
}
|
|
630
|
+
| undefined
|
|
631
|
+
>()
|
|
632
|
+
|
|
633
|
+
expectTypeOf(fn()).toEqualTypeOf<Promise<'failed' | 'success'>>()
|
|
634
|
+
})
|
|
635
|
+
|
|
636
|
+
test('incrementally building createServerFn with multiple middleware calls', () => {
|
|
637
|
+
const middleware1 = createMiddleware({ type: 'function' }).server(
|
|
638
|
+
({ next }) => {
|
|
639
|
+
return next({ context: { a: 'a' } as const })
|
|
640
|
+
},
|
|
641
|
+
)
|
|
642
|
+
|
|
643
|
+
const middleware2 = createMiddleware({ type: 'function' }).server(
|
|
644
|
+
({ next }) => {
|
|
645
|
+
return next({ context: { b: 'b' } as const })
|
|
646
|
+
},
|
|
647
|
+
)
|
|
648
|
+
|
|
649
|
+
const middleware3 = createMiddleware({ type: 'function' }).server(
|
|
650
|
+
({ next }) => {
|
|
651
|
+
return next({ context: { c: 'c' } as const })
|
|
652
|
+
},
|
|
653
|
+
)
|
|
654
|
+
|
|
655
|
+
const builderWithMw1 = createServerFn({ method: 'GET' }).middleware([
|
|
656
|
+
middleware1,
|
|
657
|
+
])
|
|
658
|
+
|
|
659
|
+
expectTypeOf(builderWithMw1).toHaveProperty('handler')
|
|
660
|
+
expectTypeOf(builderWithMw1).toHaveProperty('inputValidator')
|
|
661
|
+
expectTypeOf(builderWithMw1).toHaveProperty('middleware')
|
|
662
|
+
|
|
663
|
+
builderWithMw1.handler((options) => {
|
|
664
|
+
expectTypeOf(options).toEqualTypeOf<{
|
|
665
|
+
context: {
|
|
666
|
+
readonly a: 'a'
|
|
667
|
+
}
|
|
668
|
+
data: undefined
|
|
669
|
+
method: 'GET'
|
|
670
|
+
serverFnMeta: ServerFnMeta
|
|
671
|
+
}>()
|
|
672
|
+
})
|
|
673
|
+
|
|
674
|
+
// overrides method
|
|
675
|
+
const builderWithMw2 = builderWithMw1({ method: 'POST' }).middleware([
|
|
676
|
+
middleware2,
|
|
677
|
+
])
|
|
678
|
+
|
|
679
|
+
expectTypeOf(builderWithMw2).toHaveProperty('handler')
|
|
680
|
+
expectTypeOf(builderWithMw2).toHaveProperty('inputValidator')
|
|
681
|
+
expectTypeOf(builderWithMw2).toHaveProperty('middleware')
|
|
682
|
+
|
|
683
|
+
builderWithMw2.handler((options) => {
|
|
684
|
+
expectTypeOf(options).toEqualTypeOf<{
|
|
685
|
+
context: {
|
|
686
|
+
readonly a: 'a'
|
|
687
|
+
readonly b: 'b'
|
|
688
|
+
}
|
|
689
|
+
data: undefined
|
|
690
|
+
method: 'POST'
|
|
691
|
+
serverFnMeta: ServerFnMeta
|
|
692
|
+
}>()
|
|
693
|
+
})
|
|
694
|
+
|
|
695
|
+
// overrides method again
|
|
696
|
+
const builderWithMw3 = builderWithMw2({ method: 'GET' }).middleware([
|
|
697
|
+
middleware3,
|
|
698
|
+
])
|
|
699
|
+
|
|
700
|
+
expectTypeOf(builderWithMw3).toHaveProperty('handler')
|
|
701
|
+
expectTypeOf(builderWithMw3).toHaveProperty('inputValidator')
|
|
702
|
+
expectTypeOf(builderWithMw3).toHaveProperty('middleware')
|
|
703
|
+
|
|
704
|
+
builderWithMw3.handler((options) => {
|
|
705
|
+
expectTypeOf(options).toEqualTypeOf<{
|
|
706
|
+
context: {
|
|
707
|
+
readonly a: 'a'
|
|
708
|
+
readonly b: 'b'
|
|
709
|
+
readonly c: 'c'
|
|
710
|
+
}
|
|
711
|
+
data: undefined
|
|
712
|
+
method: 'GET'
|
|
713
|
+
|
|
714
|
+
serverFnMeta: ServerFnMeta
|
|
715
|
+
}>()
|
|
716
|
+
})
|
|
717
|
+
})
|
|
718
|
+
|
|
719
|
+
test('compose middlewares and server function factories', () => {
|
|
720
|
+
const middleware1 = createMiddleware({ type: 'function' }).server(
|
|
721
|
+
({ next }) => {
|
|
722
|
+
return next({ context: { a: 'a' } as const })
|
|
723
|
+
},
|
|
724
|
+
)
|
|
725
|
+
|
|
726
|
+
const middleware2 = createMiddleware({ type: 'function' }).server(
|
|
727
|
+
({ next }) => {
|
|
728
|
+
return next({ context: { b: 'b' } as const })
|
|
729
|
+
},
|
|
730
|
+
)
|
|
731
|
+
|
|
732
|
+
const builderWithMw1 = createServerFn().middleware([middleware1])
|
|
733
|
+
|
|
734
|
+
const composedBuilder = createServerFn({ method: 'GET' }).middleware([
|
|
735
|
+
middleware2,
|
|
736
|
+
builderWithMw1,
|
|
737
|
+
])
|
|
738
|
+
|
|
739
|
+
composedBuilder.handler((options) => {
|
|
740
|
+
expectTypeOf(options).toEqualTypeOf<{
|
|
741
|
+
context: {
|
|
742
|
+
readonly a: 'a'
|
|
743
|
+
readonly b: 'b'
|
|
744
|
+
}
|
|
745
|
+
data: undefined
|
|
746
|
+
method: 'GET'
|
|
747
|
+
serverFnMeta: ServerFnMeta
|
|
748
|
+
}>()
|
|
749
|
+
})
|
|
750
|
+
})
|
|
751
|
+
|
|
752
|
+
test('createServerFn with request middleware', () => {
|
|
753
|
+
const reqMw = createMiddleware().server(({ next }) => {
|
|
754
|
+
return next()
|
|
755
|
+
})
|
|
756
|
+
const fn = createServerFn()
|
|
757
|
+
.middleware([reqMw])
|
|
758
|
+
.handler(() => ({}))
|
|
759
|
+
|
|
760
|
+
expectTypeOf(fn()).toEqualTypeOf<Promise<{}>>()
|
|
761
|
+
})
|
|
762
|
+
|
|
763
|
+
test('createServerFn with request middleware and function middleware', () => {
|
|
764
|
+
const reqMw = createMiddleware().server(({ next }) => {
|
|
765
|
+
return next()
|
|
766
|
+
})
|
|
767
|
+
|
|
768
|
+
const funMw = createMiddleware({ type: 'function' })
|
|
769
|
+
.inputValidator((x: string) => x)
|
|
770
|
+
.server(({ next }) => {
|
|
771
|
+
return next({ context: { a: 'a' } as const })
|
|
772
|
+
})
|
|
773
|
+
const fn = createServerFn()
|
|
774
|
+
.middleware([reqMw, funMw])
|
|
775
|
+
.handler(() => ({}))
|
|
776
|
+
|
|
777
|
+
expectTypeOf(fn({ data: 'a' })).toEqualTypeOf<Promise<{}>>()
|
|
778
|
+
})
|
|
779
|
+
|
|
780
|
+
test('createServerFn with inputValidator and request middleware', () => {
|
|
781
|
+
const loggingMiddleware = createMiddleware().server(async ({ next }) => {
|
|
782
|
+
console.log('Logging middleware executed on the server')
|
|
783
|
+
const result = await next()
|
|
784
|
+
return result
|
|
785
|
+
})
|
|
786
|
+
|
|
787
|
+
const fn = createServerFn()
|
|
788
|
+
.middleware([loggingMiddleware])
|
|
789
|
+
.inputValidator(({ userName }: { userName: string }) => {
|
|
790
|
+
return { userName }
|
|
791
|
+
})
|
|
792
|
+
.handler(async ({ data }) => {
|
|
793
|
+
return data.userName
|
|
794
|
+
})
|
|
795
|
+
|
|
796
|
+
expectTypeOf(fn({ data: { userName: 'test' } })).toEqualTypeOf<
|
|
797
|
+
Promise<string>
|
|
798
|
+
>()
|
|
799
|
+
})
|
|
800
|
+
|
|
801
|
+
test('createServerFn has TSS_SERVER_FUNCTION symbol set', () => {
|
|
802
|
+
const fn = createServerFn().handler(() => ({}))
|
|
803
|
+
expectTypeOf(fn).toHaveProperty(TSS_SERVER_FUNCTION)
|
|
804
|
+
expectTypeOf(fn[TSS_SERVER_FUNCTION]).toEqualTypeOf<true>()
|
|
805
|
+
})
|
|
806
|
+
|
|
807
|
+
test('createServerFn fetcher itself is serializable', () => {
|
|
808
|
+
const fn1 = createServerFn().handler(() => ({}))
|
|
809
|
+
const fn2 = createServerFn().handler(() => fn1)
|
|
810
|
+
})
|
|
811
|
+
|
|
812
|
+
test('createServerFn returns async Response', () => {
|
|
813
|
+
const serverFn = createServerFn().handler(async () => {
|
|
814
|
+
return new Response(new Blob([JSON.stringify({ a: 1 })]), {
|
|
815
|
+
status: 200,
|
|
816
|
+
headers: {
|
|
817
|
+
'Content-Type': 'application/json',
|
|
818
|
+
},
|
|
819
|
+
})
|
|
820
|
+
})
|
|
821
|
+
|
|
822
|
+
expectTypeOf(serverFn()).toEqualTypeOf<Promise<Response>>()
|
|
823
|
+
})
|
|
824
|
+
|
|
825
|
+
test('createServerFn returns sync Response', () => {
|
|
826
|
+
const serverFn = createServerFn().handler(() => {
|
|
827
|
+
return new Response(new Blob([JSON.stringify({ a: 1 })]), {
|
|
828
|
+
status: 200,
|
|
829
|
+
headers: {
|
|
830
|
+
'Content-Type': 'application/json',
|
|
831
|
+
},
|
|
832
|
+
})
|
|
833
|
+
})
|
|
834
|
+
|
|
835
|
+
expectTypeOf(serverFn()).toEqualTypeOf<Promise<Response>>()
|
|
836
|
+
})
|
|
837
|
+
|
|
838
|
+
test('createServerFn returns async array', () => {
|
|
839
|
+
const result: Array<{ a: number }> = [{ a: 1 }]
|
|
840
|
+
const serverFn = createServerFn({ method: 'GET' }).handler(async () => {
|
|
841
|
+
return result
|
|
842
|
+
})
|
|
843
|
+
|
|
844
|
+
expectTypeOf(serverFn()).toEqualTypeOf<Promise<Array<{ a: number }>>>()
|
|
845
|
+
})
|
|
846
|
+
|
|
847
|
+
test('createServerFn returns sync array', () => {
|
|
848
|
+
const result: Array<{ a: number }> = [{ a: 1 }]
|
|
849
|
+
const serverFn = createServerFn({ method: 'GET' }).handler(() => {
|
|
850
|
+
return result
|
|
851
|
+
})
|
|
852
|
+
|
|
853
|
+
expectTypeOf(serverFn()).toEqualTypeOf<Promise<Array<{ a: number }>>>()
|
|
854
|
+
})
|
|
855
|
+
|
|
856
|
+
test('createServerFn respects TsrSerializable', () => {
|
|
857
|
+
type MyCustomType = { f: () => void; value: string }
|
|
858
|
+
type MyCustomTypeSerializable = MyCustomType & TsrSerializable
|
|
859
|
+
const fn1 = createServerFn().handler(() => {
|
|
860
|
+
const custom: MyCustomType = { f: () => {}, value: 'test' }
|
|
861
|
+
return { nested: { custom: custom as MyCustomTypeSerializable } }
|
|
862
|
+
})
|
|
863
|
+
expectTypeOf(fn1()).toEqualTypeOf<
|
|
864
|
+
Promise<{ nested: { custom: MyCustomTypeSerializable } }>
|
|
865
|
+
>()
|
|
866
|
+
})
|