@tanstack/start-client-core 1.114.4 → 1.114.5

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 (62) hide show
  1. package/dist/cjs/createIsomorphicFn.cjs +7 -0
  2. package/dist/cjs/createIsomorphicFn.cjs.map +1 -0
  3. package/dist/cjs/createIsomorphicFn.d.cts +12 -0
  4. package/dist/cjs/createMiddleware.cjs +34 -0
  5. package/dist/cjs/createMiddleware.cjs.map +1 -0
  6. package/dist/cjs/createMiddleware.d.cts +131 -0
  7. package/dist/cjs/createServerFn.cjs +227 -0
  8. package/dist/cjs/createServerFn.cjs.map +1 -0
  9. package/dist/cjs/createServerFn.d.cts +152 -0
  10. package/dist/cjs/envOnly.cjs +7 -0
  11. package/dist/cjs/envOnly.cjs.map +1 -0
  12. package/dist/cjs/envOnly.d.cts +4 -0
  13. package/dist/cjs/index.cjs +22 -0
  14. package/dist/cjs/index.cjs.map +1 -1
  15. package/dist/cjs/index.d.cts +8 -0
  16. package/dist/cjs/json.cjs +14 -0
  17. package/dist/cjs/json.cjs.map +1 -0
  18. package/dist/cjs/json.d.cts +2 -0
  19. package/dist/cjs/registerGlobalMiddleware.cjs +9 -0
  20. package/dist/cjs/registerGlobalMiddleware.cjs.map +1 -0
  21. package/dist/cjs/registerGlobalMiddleware.d.cts +5 -0
  22. package/dist/cjs/tests/createIsomorphicFn.test-d.d.cts +1 -0
  23. package/dist/cjs/tests/createServerMiddleware.test-d.d.cts +1 -0
  24. package/dist/cjs/tests/envOnly.test-d.d.cts +1 -0
  25. package/dist/cjs/tests/json.test.d.cts +1 -0
  26. package/dist/esm/createIsomorphicFn.d.ts +12 -0
  27. package/dist/esm/createIsomorphicFn.js +7 -0
  28. package/dist/esm/createIsomorphicFn.js.map +1 -0
  29. package/dist/esm/createMiddleware.d.ts +131 -0
  30. package/dist/esm/createMiddleware.js +34 -0
  31. package/dist/esm/createMiddleware.js.map +1 -0
  32. package/dist/esm/createServerFn.d.ts +152 -0
  33. package/dist/esm/createServerFn.js +206 -0
  34. package/dist/esm/createServerFn.js.map +1 -0
  35. package/dist/esm/envOnly.d.ts +4 -0
  36. package/dist/esm/envOnly.js +7 -0
  37. package/dist/esm/envOnly.js.map +1 -0
  38. package/dist/esm/index.d.ts +8 -0
  39. package/dist/esm/index.js +19 -0
  40. package/dist/esm/index.js.map +1 -1
  41. package/dist/esm/json.d.ts +2 -0
  42. package/dist/esm/json.js +14 -0
  43. package/dist/esm/json.js.map +1 -0
  44. package/dist/esm/registerGlobalMiddleware.d.ts +5 -0
  45. package/dist/esm/registerGlobalMiddleware.js +9 -0
  46. package/dist/esm/registerGlobalMiddleware.js.map +1 -0
  47. package/dist/esm/tests/createIsomorphicFn.test-d.d.ts +1 -0
  48. package/dist/esm/tests/createServerMiddleware.test-d.d.ts +1 -0
  49. package/dist/esm/tests/envOnly.test-d.d.ts +1 -0
  50. package/dist/esm/tests/json.test.d.ts +1 -0
  51. package/package.json +2 -1
  52. package/src/createIsomorphicFn.ts +36 -0
  53. package/src/createMiddleware.ts +595 -0
  54. package/src/createServerFn.ts +700 -0
  55. package/src/envOnly.ts +9 -0
  56. package/src/index.tsx +73 -0
  57. package/src/json.ts +15 -0
  58. package/src/registerGlobalMiddleware.ts +9 -0
  59. package/src/tests/createIsomorphicFn.test-d.ts +72 -0
  60. package/src/tests/createServerMiddleware.test-d.ts +611 -0
  61. package/src/tests/envOnly.test-d.ts +34 -0
  62. package/src/tests/json.test.ts +37 -0
@@ -0,0 +1,700 @@
1
+ import { default as invariant } from 'tiny-invariant'
2
+ import { default as warning } from 'tiny-warning'
3
+ import { startSerializer } from './serializer'
4
+ import { mergeHeaders } from './headers'
5
+ import type { Readable } from 'node:stream'
6
+ import type {
7
+ AnyValidator,
8
+ Constrain,
9
+ Expand,
10
+ ResolveValidatorInput,
11
+ SerializerParse,
12
+ SerializerStringify,
13
+ SerializerStringifyBy,
14
+ Validator,
15
+ } from '@tanstack/router-core'
16
+ import type {
17
+ AnyMiddleware,
18
+ AssignAllClientSendContext,
19
+ AssignAllServerContext,
20
+ IntersectAllValidatorInputs,
21
+ IntersectAllValidatorOutputs,
22
+ MiddlewareClientFnResult,
23
+ MiddlewareServerFnResult,
24
+ } from './createMiddleware'
25
+
26
+ export interface JsonResponse<TData> extends Response {
27
+ json: () => Promise<TData>
28
+ }
29
+
30
+ export type CompiledFetcherFnOptions = {
31
+ method: Method
32
+ data: unknown
33
+ response?: ServerFnResponseType
34
+ headers?: HeadersInit
35
+ signal?: AbortSignal
36
+ context?: any
37
+ }
38
+
39
+ export type Fetcher<
40
+ TMiddlewares,
41
+ TValidator,
42
+ TResponse,
43
+ TServerFnResponseType extends ServerFnResponseType,
44
+ > =
45
+ undefined extends IntersectAllValidatorInputs<TMiddlewares, TValidator>
46
+ ? OptionalFetcher<
47
+ TMiddlewares,
48
+ TValidator,
49
+ TResponse,
50
+ TServerFnResponseType
51
+ >
52
+ : RequiredFetcher<
53
+ TMiddlewares,
54
+ TValidator,
55
+ TResponse,
56
+ TServerFnResponseType
57
+ >
58
+
59
+ export interface FetcherBase {
60
+ url: string
61
+ __executeServer: (opts: {
62
+ method: Method
63
+ response?: ServerFnResponseType
64
+ data: unknown
65
+ headers?: HeadersInit
66
+ context?: any
67
+ signal: AbortSignal
68
+ }) => Promise<unknown>
69
+ }
70
+
71
+ export type FetchResult<
72
+ TMiddlewares,
73
+ TResponse,
74
+ TServerFnResponseType extends ServerFnResponseType,
75
+ > = TServerFnResponseType extends 'raw'
76
+ ? Promise<Response>
77
+ : TServerFnResponseType extends 'full'
78
+ ? Promise<FullFetcherData<TMiddlewares, TResponse>>
79
+ : Promise<FetcherData<TResponse>>
80
+
81
+ export interface OptionalFetcher<
82
+ TMiddlewares,
83
+ TValidator,
84
+ TResponse,
85
+ TServerFnResponseType extends ServerFnResponseType,
86
+ > extends FetcherBase {
87
+ (
88
+ options?: OptionalFetcherDataOptions<TMiddlewares, TValidator>,
89
+ ): FetchResult<TMiddlewares, TResponse, TServerFnResponseType>
90
+ }
91
+
92
+ export interface RequiredFetcher<
93
+ TMiddlewares,
94
+ TValidator,
95
+ TResponse,
96
+ TServerFnResponseType extends ServerFnResponseType,
97
+ > extends FetcherBase {
98
+ (
99
+ opts: RequiredFetcherDataOptions<TMiddlewares, TValidator>,
100
+ ): FetchResult<TMiddlewares, TResponse, TServerFnResponseType>
101
+ }
102
+
103
+ export type FetcherBaseOptions = {
104
+ headers?: HeadersInit
105
+ type?: ServerFnType
106
+ signal?: AbortSignal
107
+ }
108
+
109
+ export type ServerFnType = 'static' | 'dynamic'
110
+
111
+ export interface OptionalFetcherDataOptions<TMiddlewares, TValidator>
112
+ extends FetcherBaseOptions {
113
+ data?: Expand<IntersectAllValidatorInputs<TMiddlewares, TValidator>>
114
+ }
115
+
116
+ export interface RequiredFetcherDataOptions<TMiddlewares, TValidator>
117
+ extends FetcherBaseOptions {
118
+ data: Expand<IntersectAllValidatorInputs<TMiddlewares, TValidator>>
119
+ }
120
+
121
+ export interface FullFetcherData<TMiddlewares, TResponse> {
122
+ error: unknown
123
+ result: FetcherData<TResponse>
124
+ context: AssignAllClientSendContext<TMiddlewares>
125
+ }
126
+
127
+ export type FetcherData<TResponse> =
128
+ TResponse extends JsonResponse<any>
129
+ ? SerializerParse<ReturnType<TResponse['json']>>
130
+ : SerializerParse<TResponse>
131
+
132
+ export type RscStream<T> = {
133
+ __cacheState: T
134
+ }
135
+
136
+ export type Method = 'GET' | 'POST'
137
+ export type ServerFnResponseType = 'data' | 'full' | 'raw'
138
+
139
+ // see https://h3.unjs.io/guide/event-handler#responses-types
140
+ export type RawResponse = Response | ReadableStream | Readable | null | string
141
+
142
+ export type ServerFnReturnType<
143
+ TServerFnResponseType extends ServerFnResponseType,
144
+ TResponse,
145
+ > = TServerFnResponseType extends 'raw'
146
+ ? RawResponse | Promise<RawResponse>
147
+ : Promise<SerializerStringify<TResponse>> | SerializerStringify<TResponse>
148
+ export type ServerFn<
149
+ TMethod,
150
+ TServerFnResponseType extends ServerFnResponseType,
151
+ TMiddlewares,
152
+ TValidator,
153
+ TResponse,
154
+ > = (
155
+ ctx: ServerFnCtx<TMethod, TServerFnResponseType, TMiddlewares, TValidator>,
156
+ ) => ServerFnReturnType<TServerFnResponseType, TResponse>
157
+
158
+ export interface ServerFnCtx<
159
+ TMethod,
160
+ TServerFnResponseType extends ServerFnResponseType,
161
+ TMiddlewares,
162
+ TValidator,
163
+ > {
164
+ method: TMethod
165
+ response: TServerFnResponseType
166
+ data: Expand<IntersectAllValidatorOutputs<TMiddlewares, TValidator>>
167
+ context: Expand<AssignAllServerContext<TMiddlewares>>
168
+ signal: AbortSignal
169
+ }
170
+
171
+ export type CompiledFetcherFn<
172
+ TResponse,
173
+ TServerFnResponseType extends ServerFnResponseType,
174
+ > = {
175
+ (
176
+ opts: CompiledFetcherFnOptions &
177
+ ServerFnBaseOptions<Method, TServerFnResponseType>,
178
+ ): Promise<TResponse>
179
+ url: string
180
+ }
181
+
182
+ export type ServerFnBaseOptions<
183
+ TMethod extends Method = 'GET',
184
+ TServerFnResponseType extends ServerFnResponseType = 'data',
185
+ TResponse = unknown,
186
+ TMiddlewares = unknown,
187
+ TInput = unknown,
188
+ > = {
189
+ method: TMethod
190
+ response?: TServerFnResponseType
191
+ validateClient?: boolean
192
+ middleware?: Constrain<TMiddlewares, ReadonlyArray<AnyMiddleware>>
193
+ validator?: ConstrainValidator<TInput>
194
+ extractedFn?: CompiledFetcherFn<TResponse, TServerFnResponseType>
195
+ serverFn?: ServerFn<
196
+ TMethod,
197
+ TServerFnResponseType,
198
+ TMiddlewares,
199
+ TInput,
200
+ TResponse
201
+ >
202
+ functionId: string
203
+ type: ServerFnTypeOrTypeFn<
204
+ TMethod,
205
+ TServerFnResponseType,
206
+ TMiddlewares,
207
+ AnyValidator
208
+ >
209
+ }
210
+
211
+ export type ValidatorSerializerStringify<TValidator> = Validator<
212
+ SerializerStringifyBy<
213
+ ResolveValidatorInput<TValidator>,
214
+ Date | undefined | FormData
215
+ >,
216
+ any
217
+ >
218
+
219
+ export type ConstrainValidator<TValidator> = unknown extends TValidator
220
+ ? TValidator
221
+ : Constrain<TValidator, ValidatorSerializerStringify<TValidator>>
222
+
223
+ export interface ServerFnMiddleware<
224
+ TMethod extends Method,
225
+ TServerFnResponseType extends ServerFnResponseType,
226
+ TValidator,
227
+ > {
228
+ middleware: <const TNewMiddlewares = undefined>(
229
+ middlewares: Constrain<TNewMiddlewares, ReadonlyArray<AnyMiddleware>>,
230
+ ) => ServerFnAfterMiddleware<
231
+ TMethod,
232
+ TServerFnResponseType,
233
+ TNewMiddlewares,
234
+ TValidator
235
+ >
236
+ }
237
+
238
+ export interface ServerFnAfterMiddleware<
239
+ TMethod extends Method,
240
+ TServerFnResponseType extends ServerFnResponseType,
241
+ TMiddlewares,
242
+ TValidator,
243
+ > extends ServerFnValidator<TMethod, TServerFnResponseType, TMiddlewares>,
244
+ ServerFnTyper<TMethod, TServerFnResponseType, TMiddlewares, TValidator>,
245
+ ServerFnHandler<TMethod, TServerFnResponseType, TMiddlewares, TValidator> {}
246
+
247
+ export type ValidatorFn<
248
+ TMethod extends Method,
249
+ TServerFnResponseType extends ServerFnResponseType,
250
+ TMiddlewares,
251
+ > = <TValidator>(
252
+ validator: ConstrainValidator<TValidator>,
253
+ ) => ServerFnAfterValidator<
254
+ TMethod,
255
+ TServerFnResponseType,
256
+ TMiddlewares,
257
+ TValidator
258
+ >
259
+
260
+ export interface ServerFnValidator<
261
+ TMethod extends Method,
262
+ TServerFnResponseType extends ServerFnResponseType,
263
+ TMiddlewares,
264
+ > {
265
+ validator: ValidatorFn<TMethod, TServerFnResponseType, TMiddlewares>
266
+ }
267
+
268
+ export interface ServerFnAfterValidator<
269
+ TMethod extends Method,
270
+ TServerFnResponseType extends ServerFnResponseType,
271
+ TMiddlewares,
272
+ TValidator,
273
+ > extends ServerFnMiddleware<TMethod, TServerFnResponseType, TValidator>,
274
+ ServerFnTyper<TMethod, TServerFnResponseType, TMiddlewares, TValidator>,
275
+ ServerFnHandler<TMethod, TServerFnResponseType, TMiddlewares, TValidator> {}
276
+
277
+ // Typer
278
+ export interface ServerFnTyper<
279
+ TMethod extends Method,
280
+ TServerFnResponseType extends ServerFnResponseType,
281
+ TMiddlewares,
282
+ TValidator,
283
+ > {
284
+ type: (
285
+ typer: ServerFnTypeOrTypeFn<
286
+ TMethod,
287
+ TServerFnResponseType,
288
+ TMiddlewares,
289
+ TValidator
290
+ >,
291
+ ) => ServerFnAfterTyper<
292
+ TMethod,
293
+ TServerFnResponseType,
294
+ TMiddlewares,
295
+ TValidator
296
+ >
297
+ }
298
+
299
+ export type ServerFnTypeOrTypeFn<
300
+ TMethod extends Method,
301
+ TServerFnResponseType extends ServerFnResponseType,
302
+ TMiddlewares,
303
+ TValidator,
304
+ > =
305
+ | ServerFnType
306
+ | ((
307
+ ctx: ServerFnCtx<
308
+ TMethod,
309
+ TServerFnResponseType,
310
+ TMiddlewares,
311
+ TValidator
312
+ >,
313
+ ) => ServerFnType)
314
+
315
+ export interface ServerFnAfterTyper<
316
+ TMethod extends Method,
317
+ TServerFnResponseType extends ServerFnResponseType,
318
+ TMiddlewares,
319
+ TValidator,
320
+ > extends ServerFnHandler<
321
+ TMethod,
322
+ TServerFnResponseType,
323
+ TMiddlewares,
324
+ TValidator
325
+ > {}
326
+
327
+ // Handler
328
+ export interface ServerFnHandler<
329
+ TMethod extends Method,
330
+ TServerFnResponseType extends ServerFnResponseType,
331
+ TMiddlewares,
332
+ TValidator,
333
+ > {
334
+ handler: <TNewResponse>(
335
+ fn?: ServerFn<
336
+ TMethod,
337
+ TServerFnResponseType,
338
+ TMiddlewares,
339
+ TValidator,
340
+ TNewResponse
341
+ >,
342
+ ) => Fetcher<TMiddlewares, TValidator, TNewResponse, TServerFnResponseType>
343
+ }
344
+
345
+ export interface ServerFnBuilder<
346
+ TMethod extends Method = 'GET',
347
+ TServerFnResponseType extends ServerFnResponseType = 'data',
348
+ > extends ServerFnMiddleware<TMethod, TServerFnResponseType, undefined>,
349
+ ServerFnValidator<TMethod, TServerFnResponseType, undefined>,
350
+ ServerFnTyper<TMethod, TServerFnResponseType, undefined, undefined>,
351
+ ServerFnHandler<TMethod, TServerFnResponseType, undefined, undefined> {
352
+ options: ServerFnBaseOptions<
353
+ TMethod,
354
+ TServerFnResponseType,
355
+ unknown,
356
+ undefined,
357
+ undefined
358
+ >
359
+ }
360
+
361
+ export type StaticCachedResult = {
362
+ ctx?: {
363
+ result: any
364
+ context: any
365
+ }
366
+ error?: any
367
+ }
368
+
369
+ export type ServerFnStaticCache = {
370
+ getItem: (
371
+ ctx: ServerFnMiddlewareResult,
372
+ ) => StaticCachedResult | Promise<StaticCachedResult | undefined>
373
+ setItem: (
374
+ ctx: ServerFnMiddlewareResult,
375
+ response: StaticCachedResult,
376
+ ) => Promise<void>
377
+ fetchItem: (
378
+ ctx: ServerFnMiddlewareResult,
379
+ ) => StaticCachedResult | Promise<StaticCachedResult | undefined>
380
+ }
381
+
382
+ export let serverFnStaticCache: ServerFnStaticCache | undefined
383
+
384
+ export function setServerFnStaticCache(
385
+ cache?: ServerFnStaticCache | (() => ServerFnStaticCache | undefined),
386
+ ) {
387
+ const previousCache = serverFnStaticCache
388
+ serverFnStaticCache = typeof cache === 'function' ? cache() : cache
389
+
390
+ return () => {
391
+ serverFnStaticCache = previousCache
392
+ }
393
+ }
394
+
395
+ export function createServerFnStaticCache(
396
+ serverFnStaticCache: ServerFnStaticCache,
397
+ ) {
398
+ return serverFnStaticCache
399
+ }
400
+
401
+ setServerFnStaticCache(() => {
402
+ const getStaticCacheUrl = (
403
+ options: ServerFnMiddlewareResult,
404
+ hash: string,
405
+ ) => {
406
+ return `/__tsr/staticServerFnCache/${options.functionId}__${hash}.json`
407
+ }
408
+
409
+ const jsonToFilenameSafeString = (json: any) => {
410
+ // Custom replacer to sort keys
411
+ const sortedKeysReplacer = (key: string, value: any) =>
412
+ value && typeof value === 'object' && !Array.isArray(value)
413
+ ? Object.keys(value)
414
+ .sort()
415
+ .reduce((acc: any, curr: string) => {
416
+ acc[curr] = value[curr]
417
+ return acc
418
+ }, {})
419
+ : value
420
+
421
+ // Convert JSON to string with sorted keys
422
+ const jsonString = JSON.stringify(json ?? '', sortedKeysReplacer)
423
+
424
+ // Replace characters invalid in filenames
425
+ return jsonString
426
+ .replace(/[/\\?%*:|"<>]/g, '-') // Replace invalid characters with a dash
427
+ .replace(/\s+/g, '_') // Optionally replace whitespace with underscores
428
+ }
429
+
430
+ const staticClientCache =
431
+ typeof document !== 'undefined' ? new Map<string, any>() : null
432
+
433
+ return createServerFnStaticCache({
434
+ getItem: async (ctx) => {
435
+ if (typeof document === 'undefined') {
436
+ const hash = jsonToFilenameSafeString(ctx.data)
437
+ const url = getStaticCacheUrl(ctx, hash)
438
+ const publicUrl = process.env.TSS_OUTPUT_PUBLIC_DIR!
439
+
440
+ // Use fs instead of fetch to read from filesystem
441
+ const { promises: fs } = await import('node:fs')
442
+ const path = await import('node:path')
443
+ const filePath = path.join(publicUrl, url)
444
+
445
+ const [cachedResult, readError] = await fs
446
+ .readFile(filePath, 'utf-8')
447
+ .then((c) => [
448
+ startSerializer.parse(c) as {
449
+ ctx: unknown
450
+ error: any
451
+ },
452
+ null,
453
+ ])
454
+ .catch((e) => [null, e])
455
+
456
+ if (readError && readError.code !== 'ENOENT') {
457
+ throw readError
458
+ }
459
+
460
+ return cachedResult as StaticCachedResult
461
+ }
462
+
463
+ return undefined
464
+ },
465
+ setItem: async (ctx, response) => {
466
+ const { promises: fs } = await import('node:fs')
467
+ const path = await import('node:path')
468
+
469
+ const hash = jsonToFilenameSafeString(ctx.data)
470
+ const url = getStaticCacheUrl(ctx, hash)
471
+ const publicUrl = process.env.TSS_OUTPUT_PUBLIC_DIR!
472
+ const filePath = path.join(publicUrl, url)
473
+
474
+ // Ensure the directory exists
475
+ await fs.mkdir(path.dirname(filePath), { recursive: true })
476
+
477
+ // Store the result with fs
478
+ await fs.writeFile(filePath, startSerializer.stringify(response))
479
+ },
480
+ fetchItem: async (ctx) => {
481
+ const hash = jsonToFilenameSafeString(ctx.data)
482
+ const url = getStaticCacheUrl(ctx, hash)
483
+
484
+ let result: any = staticClientCache?.get(url)
485
+
486
+ if (!result) {
487
+ result = await fetch(url, {
488
+ method: 'GET',
489
+ })
490
+ .then((r) => r.text())
491
+ .then((d) => startSerializer.parse(d))
492
+
493
+ staticClientCache?.set(url, result)
494
+ }
495
+
496
+ return result
497
+ },
498
+ })
499
+ })
500
+
501
+ export function extractFormDataContext(formData: FormData) {
502
+ const serializedContext = formData.get('__TSR_CONTEXT')
503
+ formData.delete('__TSR_CONTEXT')
504
+
505
+ if (typeof serializedContext !== 'string') {
506
+ return {
507
+ context: {},
508
+ data: formData,
509
+ }
510
+ }
511
+
512
+ try {
513
+ const context = startSerializer.parse(serializedContext)
514
+ return {
515
+ context,
516
+ data: formData,
517
+ }
518
+ } catch {
519
+ return {
520
+ data: formData,
521
+ }
522
+ }
523
+ }
524
+
525
+ export function flattenMiddlewares(
526
+ middlewares: Array<AnyMiddleware>,
527
+ ): Array<AnyMiddleware> {
528
+ const seen = new Set<AnyMiddleware>()
529
+ const flattened: Array<AnyMiddleware> = []
530
+
531
+ const recurse = (middleware: Array<AnyMiddleware>) => {
532
+ middleware.forEach((m) => {
533
+ if (m.options.middleware) {
534
+ recurse(m.options.middleware)
535
+ }
536
+
537
+ if (!seen.has(m)) {
538
+ seen.add(m)
539
+ flattened.push(m)
540
+ }
541
+ })
542
+ }
543
+
544
+ recurse(middlewares)
545
+
546
+ return flattened
547
+ }
548
+
549
+ export type ServerFnMiddlewareOptions = {
550
+ method: Method
551
+ response?: ServerFnResponseType
552
+ data: any
553
+ headers?: HeadersInit
554
+ signal?: AbortSignal
555
+ sendContext?: any
556
+ context?: any
557
+ type: ServerFnTypeOrTypeFn<any, any, any, any>
558
+ functionId: string
559
+ }
560
+
561
+ export type ServerFnMiddlewareResult = ServerFnMiddlewareOptions & {
562
+ result?: unknown
563
+ error?: unknown
564
+ type: ServerFnTypeOrTypeFn<any, any, any, any>
565
+ }
566
+
567
+ export type NextFn = (
568
+ ctx: ServerFnMiddlewareResult,
569
+ ) => Promise<ServerFnMiddlewareResult>
570
+
571
+ export type MiddlewareFn = (
572
+ ctx: ServerFnMiddlewareOptions & {
573
+ next: NextFn
574
+ },
575
+ ) => Promise<ServerFnMiddlewareResult>
576
+
577
+ export const applyMiddleware = async (
578
+ middlewareFn: MiddlewareFn,
579
+ ctx: ServerFnMiddlewareOptions,
580
+ nextFn: NextFn,
581
+ ) => {
582
+ return middlewareFn({
583
+ ...ctx,
584
+ next: (async (
585
+ userCtx: ServerFnMiddlewareResult | undefined = {} as any,
586
+ ) => {
587
+ // Return the next middleware
588
+ return nextFn({
589
+ ...ctx,
590
+ ...userCtx,
591
+ context: {
592
+ ...ctx.context,
593
+ ...userCtx.context,
594
+ },
595
+ sendContext: {
596
+ ...ctx.sendContext,
597
+ ...(userCtx.sendContext ?? {}),
598
+ },
599
+ headers: mergeHeaders(ctx.headers, userCtx.headers),
600
+ result:
601
+ userCtx.result !== undefined
602
+ ? userCtx.result
603
+ : ctx.response === 'raw'
604
+ ? userCtx
605
+ : (ctx as any).result,
606
+ error: userCtx.error ?? (ctx as any).error,
607
+ })
608
+ }) as any,
609
+ } as any)
610
+ }
611
+
612
+ export function execValidator(
613
+ validator: AnyValidator,
614
+ input: unknown,
615
+ ): unknown {
616
+ if (validator == null) return {}
617
+
618
+ if ('~standard' in validator) {
619
+ const result = validator['~standard'].validate(input)
620
+
621
+ if (result instanceof Promise)
622
+ throw new Error('Async validation not supported')
623
+
624
+ if (result.issues)
625
+ throw new Error(JSON.stringify(result.issues, undefined, 2))
626
+
627
+ return result.value
628
+ }
629
+
630
+ if ('parse' in validator) {
631
+ return validator.parse(input)
632
+ }
633
+
634
+ if (typeof validator === 'function') {
635
+ return validator(input)
636
+ }
637
+
638
+ throw new Error('Invalid validator type!')
639
+ }
640
+
641
+ export function serverFnBaseToMiddleware(
642
+ options: ServerFnBaseOptions<any, any, any, any, any>,
643
+ ): AnyMiddleware {
644
+ return {
645
+ _types: undefined!,
646
+ options: {
647
+ validator: options.validator,
648
+ validateClient: options.validateClient,
649
+ client: async ({ next, sendContext, ...ctx }) => {
650
+ const payload = {
651
+ ...ctx,
652
+ // switch the sendContext over to context
653
+ context: sendContext,
654
+ type: typeof ctx.type === 'function' ? ctx.type(ctx) : ctx.type,
655
+ } as any
656
+
657
+ if (
658
+ ctx.type === 'static' &&
659
+ process.env.NODE_ENV === 'production' &&
660
+ typeof document !== 'undefined'
661
+ ) {
662
+ invariant(
663
+ serverFnStaticCache,
664
+ 'serverFnStaticCache.fetchItem is not available!',
665
+ )
666
+
667
+ const result = await serverFnStaticCache.fetchItem(payload)
668
+
669
+ if (result) {
670
+ if (result.error) {
671
+ throw result.error
672
+ }
673
+
674
+ return next(result.ctx)
675
+ }
676
+
677
+ warning(
678
+ result,
679
+ `No static cache item found for ${payload.functionId}__${JSON.stringify(payload.data)}, falling back to server function...`,
680
+ )
681
+ }
682
+
683
+ // Execute the extracted function
684
+ // but not before serializing the context
685
+ const res = await options.extractedFn?.(payload)
686
+
687
+ return next(res) as unknown as MiddlewareClientFnResult<any, any, any>
688
+ },
689
+ server: async ({ next, ...ctx }) => {
690
+ // Execute the server function
691
+ const result = await options.serverFn?.(ctx)
692
+
693
+ return next({
694
+ ...ctx,
695
+ result,
696
+ } as any) as unknown as MiddlewareServerFnResult<any, any, any, any>
697
+ },
698
+ },
699
+ }
700
+ }
package/src/envOnly.ts ADDED
@@ -0,0 +1,9 @@
1
+ type EnvOnlyFn = <TFn extends (...args: Array<any>) => any>(fn: TFn) => TFn
2
+
3
+ // A function that will only be available in the server build
4
+ // If called on the client, it will throw an error
5
+ export const serverOnly: EnvOnlyFn = (fn) => fn
6
+
7
+ // A function that will only be available in the client build
8
+ // If called on the server, it will throw an error
9
+ export const clientOnly: EnvOnlyFn = (fn) => fn