@tanstack/start-client-core 1.121.0-alpha.27 → 1.121.0-alpha.28
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/dist/esm/createIsomorphicFn.js +6 -1
- package/dist/esm/createIsomorphicFn.js.map +1 -1
- package/dist/esm/createMiddleware.d.ts +8 -7
- package/dist/esm/createMiddleware.js.map +1 -1
- package/dist/esm/createServerFn.d.ts +7 -32
- package/dist/esm/createServerFn.js +15 -147
- package/dist/esm/createServerFn.js.map +1 -1
- package/dist/esm/envOnly.js.map +1 -1
- package/dist/esm/index.d.ts +6 -6
- package/dist/esm/index.js +2 -5
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/registerGlobalMiddleware.js.map +1 -1
- package/dist/esm/serializer.d.ts +22 -1
- package/dist/esm/serializer.js.map +1 -1
- package/package.json +3 -6
- package/src/createIsomorphicFn.ts +6 -1
- package/src/createMiddleware.ts +11 -10
- package/src/createServerFn.ts +28 -328
- package/src/index.tsx +19 -19
- package/src/serializer.ts +32 -3
- package/src/tests/createServerFn.test-d.ts +0 -6
- package/src/tests/{transformer.test.tsx → serializer.test.tsx} +36 -32
- package/dist/cjs/createIsomorphicFn.cjs +0 -7
- package/dist/cjs/createIsomorphicFn.cjs.map +0 -1
- package/dist/cjs/createIsomorphicFn.d.cts +0 -12
- package/dist/cjs/createMiddleware.cjs +0 -37
- package/dist/cjs/createMiddleware.cjs.map +0 -1
- package/dist/cjs/createMiddleware.d.cts +0 -175
- package/dist/cjs/createServerFn.cjs +0 -378
- package/dist/cjs/createServerFn.cjs.map +0 -1
- package/dist/cjs/createServerFn.d.cts +0 -159
- package/dist/cjs/envOnly.cjs +0 -7
- package/dist/cjs/envOnly.cjs.map +0 -1
- package/dist/cjs/envOnly.d.cts +0 -4
- package/dist/cjs/headers.cjs +0 -30
- package/dist/cjs/headers.cjs.map +0 -1
- package/dist/cjs/headers.d.cts +0 -5
- package/dist/cjs/index.cjs +0 -33
- package/dist/cjs/index.cjs.map +0 -1
- package/dist/cjs/index.d.cts +0 -11
- package/dist/cjs/json.cjs +0 -14
- package/dist/cjs/json.cjs.map +0 -1
- package/dist/cjs/json.d.cts +0 -2
- package/dist/cjs/registerGlobalMiddleware.cjs +0 -9
- package/dist/cjs/registerGlobalMiddleware.cjs.map +0 -1
- package/dist/cjs/registerGlobalMiddleware.d.cts +0 -5
- package/dist/cjs/serializer.cjs +0 -152
- package/dist/cjs/serializer.cjs.map +0 -1
- package/dist/cjs/serializer.d.cts +0 -2
- package/dist/cjs/ssr-client.cjs +0 -131
- package/dist/cjs/ssr-client.cjs.map +0 -1
- package/dist/cjs/ssr-client.d.cts +0 -65
- package/dist/cjs/tests/createServerFn.test-d.d.cts +0 -1
- package/dist/cjs/tests/createServerMiddleware.test-d.d.cts +0 -1
- package/dist/cjs/tests/envOnly.test-d.d.cts +0 -1
- package/dist/cjs/tests/json.test.d.cts +0 -1
- package/dist/cjs/tests/transformer.test.d.cts +0 -1
- package/dist/esm/headers.d.ts +0 -5
- package/dist/esm/headers.js +0 -30
- package/dist/esm/headers.js.map +0 -1
- package/dist/esm/json.d.ts +0 -2
- package/dist/esm/json.js +0 -14
- package/dist/esm/json.js.map +0 -1
- package/dist/esm/ssr-client.d.ts +0 -65
- package/dist/esm/ssr-client.js +0 -131
- package/dist/esm/ssr-client.js.map +0 -1
- package/dist/esm/tests/json.test.d.ts +0 -1
- package/dist/esm/tests/transformer.test.d.ts +0 -1
- package/src/headers.ts +0 -50
- package/src/json.ts +0 -15
- package/src/ssr-client.tsx +0 -249
- package/src/tests/json.test.ts +0 -37
- /package/dist/{cjs/tests/createIsomorphicFn.test-d.d.cts → esm/tests/serializer.test.d.ts} +0 -0
package/src/createServerFn.ts
CHANGED
|
@@ -1,19 +1,25 @@
|
|
|
1
|
-
import { default as invariant } from 'tiny-invariant'
|
|
2
|
-
import { default as warning } from 'tiny-warning'
|
|
3
1
|
import { isNotFound, isRedirect } from '@tanstack/router-core'
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
2
|
+
import { mergeHeaders } from '@tanstack/router-core/ssr/client'
|
|
3
|
+
import { getStartContext } from '@tanstack/start-storage-context'
|
|
6
4
|
import { globalMiddleware } from './registerGlobalMiddleware'
|
|
5
|
+
|
|
6
|
+
import { startSerializer } from './serializer'
|
|
7
|
+
|
|
8
|
+
import { createIsomorphicFn } from './createIsomorphicFn'
|
|
9
|
+
import type {
|
|
10
|
+
SerializerParse,
|
|
11
|
+
SerializerStringify,
|
|
12
|
+
SerializerStringifyBy,
|
|
13
|
+
} from './serializer'
|
|
7
14
|
import type {
|
|
15
|
+
AnyRouter,
|
|
8
16
|
AnyValidator,
|
|
9
17
|
Constrain,
|
|
10
18
|
Expand,
|
|
11
19
|
ResolveValidatorInput,
|
|
12
|
-
SerializerParse,
|
|
13
|
-
SerializerStringify,
|
|
14
|
-
SerializerStringifyBy,
|
|
15
20
|
Validator,
|
|
16
21
|
} from '@tanstack/router-core'
|
|
22
|
+
import type { JsonResponse } from '@tanstack/router-core/ssr/client'
|
|
17
23
|
import type { Readable } from 'node:stream'
|
|
18
24
|
import type {
|
|
19
25
|
AnyFunctionMiddleware,
|
|
@@ -27,6 +33,10 @@ import type {
|
|
|
27
33
|
|
|
28
34
|
type TODO = any
|
|
29
35
|
|
|
36
|
+
const getRouterInstance = createIsomorphicFn()
|
|
37
|
+
.client(() => window.__TSR_ROUTER__!)
|
|
38
|
+
.server(() => getStartContext({ throwIfNotFound: false })?.router)
|
|
39
|
+
|
|
30
40
|
export function createServerFn<
|
|
31
41
|
TMethod extends Method,
|
|
32
42
|
TServerFnResponseType extends ServerFnResponseType = 'data',
|
|
@@ -37,7 +47,6 @@ export function createServerFn<
|
|
|
37
47
|
options?: {
|
|
38
48
|
method?: TMethod
|
|
39
49
|
response?: TServerFnResponseType
|
|
40
|
-
type?: ServerFnType
|
|
41
50
|
},
|
|
42
51
|
__opts?: ServerFnBaseOptions<
|
|
43
52
|
TMethod,
|
|
@@ -79,15 +88,6 @@ export function createServerFn<
|
|
|
79
88
|
TValidator
|
|
80
89
|
>(undefined, Object.assign(resolvedOptions, { validator })) as any
|
|
81
90
|
},
|
|
82
|
-
type: (type) => {
|
|
83
|
-
return createServerFn<
|
|
84
|
-
TMethod,
|
|
85
|
-
ServerFnResponseType,
|
|
86
|
-
TResponse,
|
|
87
|
-
TMiddlewares,
|
|
88
|
-
TValidator
|
|
89
|
-
>(undefined, Object.assign(resolvedOptions, { type })) as any
|
|
90
|
-
},
|
|
91
91
|
handler: (...args) => {
|
|
92
92
|
// This function signature changes due to AST transformations
|
|
93
93
|
// in the babel plugin. We need to cast it to the correct
|
|
@@ -129,6 +129,7 @@ export function createServerFn<
|
|
|
129
129
|
headers: opts?.headers,
|
|
130
130
|
signal: opts?.signal,
|
|
131
131
|
context: {},
|
|
132
|
+
router: getRouterInstance(),
|
|
132
133
|
}).then((d) => {
|
|
133
134
|
if (resolvedOptions.response === 'full') {
|
|
134
135
|
return d
|
|
@@ -146,70 +147,20 @@ export function createServerFn<
|
|
|
146
147
|
const opts =
|
|
147
148
|
opts_ instanceof FormData ? extractFormDataContext(opts_) : opts_
|
|
148
149
|
|
|
149
|
-
opts.type =
|
|
150
|
-
typeof resolvedOptions.type === 'function'
|
|
151
|
-
? resolvedOptions.type(opts)
|
|
152
|
-
: resolvedOptions.type
|
|
153
|
-
|
|
154
150
|
const ctx = {
|
|
155
151
|
...extractedFn,
|
|
156
152
|
...opts,
|
|
157
153
|
signal,
|
|
158
154
|
}
|
|
159
155
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
)
|
|
169
|
-
|
|
170
|
-
if (ctx.type === 'static') {
|
|
171
|
-
let response: StaticCachedResult | undefined
|
|
172
|
-
|
|
173
|
-
// If we can get the cached item, try to get it
|
|
174
|
-
if (serverFnStaticCache?.getItem) {
|
|
175
|
-
// If this throws, it's okay to let it bubble up
|
|
176
|
-
response = await serverFnStaticCache.getItem(ctx)
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
if (!response) {
|
|
180
|
-
// If there's no cached item, execute the server function
|
|
181
|
-
response = await run()
|
|
182
|
-
.then((d) => {
|
|
183
|
-
return {
|
|
184
|
-
ctx: d,
|
|
185
|
-
error: null,
|
|
186
|
-
}
|
|
187
|
-
})
|
|
188
|
-
.catch((e) => {
|
|
189
|
-
return {
|
|
190
|
-
ctx: undefined,
|
|
191
|
-
error: e,
|
|
192
|
-
}
|
|
193
|
-
})
|
|
194
|
-
|
|
195
|
-
if (serverFnStaticCache?.setItem) {
|
|
196
|
-
await serverFnStaticCache.setItem(ctx, response)
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
invariant(
|
|
201
|
-
response,
|
|
202
|
-
'No response from both server and static cache!',
|
|
203
|
-
)
|
|
204
|
-
|
|
205
|
-
if (response.error) {
|
|
206
|
-
throw response.error
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
return response.ctx
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
return run()
|
|
156
|
+
return executeMiddleware(resolvedMiddleware, 'server', ctx).then(
|
|
157
|
+
(d) => ({
|
|
158
|
+
// Only send the result and sendContext back to the client
|
|
159
|
+
result: d.result,
|
|
160
|
+
error: d.error,
|
|
161
|
+
context: d.sendContext,
|
|
162
|
+
}),
|
|
163
|
+
)
|
|
213
164
|
},
|
|
214
165
|
},
|
|
215
166
|
) as any
|
|
@@ -278,10 +229,6 @@ export async function executeMiddleware(
|
|
|
278
229
|
})
|
|
279
230
|
}
|
|
280
231
|
|
|
281
|
-
export interface JsonResponse<TData> extends Response {
|
|
282
|
-
json: () => Promise<TData>
|
|
283
|
-
}
|
|
284
|
-
|
|
285
232
|
export type CompiledFetcherFnOptions = {
|
|
286
233
|
method: Method
|
|
287
234
|
data: unknown
|
|
@@ -289,6 +236,7 @@ export type CompiledFetcherFnOptions = {
|
|
|
289
236
|
headers?: HeadersInit
|
|
290
237
|
signal?: AbortSignal
|
|
291
238
|
context?: any
|
|
239
|
+
// router?: AnyRouter
|
|
292
240
|
}
|
|
293
241
|
|
|
294
242
|
export type Fetcher<
|
|
@@ -357,12 +305,9 @@ export interface RequiredFetcher<
|
|
|
357
305
|
|
|
358
306
|
export type FetcherBaseOptions = {
|
|
359
307
|
headers?: HeadersInit
|
|
360
|
-
type?: ServerFnType
|
|
361
308
|
signal?: AbortSignal
|
|
362
309
|
}
|
|
363
310
|
|
|
364
|
-
export type ServerFnType = 'static' | 'dynamic'
|
|
365
|
-
|
|
366
311
|
export interface OptionalFetcherDataOptions<TMiddlewares, TValidator>
|
|
367
312
|
extends FetcherBaseOptions {
|
|
368
313
|
data?: Expand<IntersectAllValidatorInputs<TMiddlewares, TValidator>>
|
|
@@ -456,12 +401,6 @@ export type ServerFnBaseOptions<
|
|
|
456
401
|
TResponse
|
|
457
402
|
>
|
|
458
403
|
functionId: string
|
|
459
|
-
type: ServerFnTypeOrTypeFn<
|
|
460
|
-
TMethod,
|
|
461
|
-
TServerFnResponseType,
|
|
462
|
-
TMiddlewares,
|
|
463
|
-
AnyValidator
|
|
464
|
-
>
|
|
465
404
|
}
|
|
466
405
|
|
|
467
406
|
export type ValidatorInputStringify<TValidator> = SerializerStringifyBy<
|
|
@@ -506,7 +445,6 @@ export interface ServerFnAfterMiddleware<
|
|
|
506
445
|
TMiddlewares,
|
|
507
446
|
TValidator,
|
|
508
447
|
> extends ServerFnValidator<TMethod, TServerFnResponseType, TMiddlewares>,
|
|
509
|
-
ServerFnTyper<TMethod, TServerFnResponseType, TMiddlewares, TValidator>,
|
|
510
448
|
ServerFnHandler<TMethod, TServerFnResponseType, TMiddlewares, TValidator> {}
|
|
511
449
|
|
|
512
450
|
export type ValidatorFn<
|
|
@@ -536,47 +474,8 @@ export interface ServerFnAfterValidator<
|
|
|
536
474
|
TMiddlewares,
|
|
537
475
|
TValidator,
|
|
538
476
|
> extends ServerFnMiddleware<TMethod, TServerFnResponseType, TValidator>,
|
|
539
|
-
ServerFnTyper<TMethod, TServerFnResponseType, TMiddlewares, TValidator>,
|
|
540
477
|
ServerFnHandler<TMethod, TServerFnResponseType, TMiddlewares, TValidator> {}
|
|
541
478
|
|
|
542
|
-
// Typer
|
|
543
|
-
export interface ServerFnTyper<
|
|
544
|
-
TMethod extends Method,
|
|
545
|
-
TServerFnResponseType extends ServerFnResponseType,
|
|
546
|
-
TMiddlewares,
|
|
547
|
-
TValidator,
|
|
548
|
-
> {
|
|
549
|
-
type: (
|
|
550
|
-
typer: ServerFnTypeOrTypeFn<
|
|
551
|
-
TMethod,
|
|
552
|
-
TServerFnResponseType,
|
|
553
|
-
TMiddlewares,
|
|
554
|
-
TValidator
|
|
555
|
-
>,
|
|
556
|
-
) => ServerFnAfterTyper<
|
|
557
|
-
TMethod,
|
|
558
|
-
TServerFnResponseType,
|
|
559
|
-
TMiddlewares,
|
|
560
|
-
TValidator
|
|
561
|
-
>
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
export type ServerFnTypeOrTypeFn<
|
|
565
|
-
TMethod extends Method,
|
|
566
|
-
TServerFnResponseType extends ServerFnResponseType,
|
|
567
|
-
TMiddlewares,
|
|
568
|
-
TValidator,
|
|
569
|
-
> =
|
|
570
|
-
| ServerFnType
|
|
571
|
-
| ((
|
|
572
|
-
ctx: ServerFnCtx<
|
|
573
|
-
TMethod,
|
|
574
|
-
TServerFnResponseType,
|
|
575
|
-
TMiddlewares,
|
|
576
|
-
TValidator
|
|
577
|
-
>,
|
|
578
|
-
) => ServerFnType)
|
|
579
|
-
|
|
580
479
|
export interface ServerFnAfterTyper<
|
|
581
480
|
TMethod extends Method,
|
|
582
481
|
TServerFnResponseType extends ServerFnResponseType,
|
|
@@ -612,7 +511,6 @@ export interface ServerFnBuilder<
|
|
|
612
511
|
TServerFnResponseType extends ServerFnResponseType = 'data',
|
|
613
512
|
> extends ServerFnMiddleware<TMethod, TServerFnResponseType, undefined>,
|
|
614
513
|
ServerFnValidator<TMethod, TServerFnResponseType, undefined>,
|
|
615
|
-
ServerFnTyper<TMethod, TServerFnResponseType, undefined, undefined>,
|
|
616
514
|
ServerFnHandler<TMethod, TServerFnResponseType, undefined, undefined> {
|
|
617
515
|
options: ServerFnBaseOptions<
|
|
618
516
|
TMethod,
|
|
@@ -623,176 +521,6 @@ export interface ServerFnBuilder<
|
|
|
623
521
|
>
|
|
624
522
|
}
|
|
625
523
|
|
|
626
|
-
export type StaticCachedResult = {
|
|
627
|
-
ctx?: {
|
|
628
|
-
result: any
|
|
629
|
-
context: any
|
|
630
|
-
}
|
|
631
|
-
error?: any
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
export type ServerFnStaticCache = {
|
|
635
|
-
getItem: (
|
|
636
|
-
ctx: ServerFnMiddlewareResult,
|
|
637
|
-
) => StaticCachedResult | Promise<StaticCachedResult | undefined>
|
|
638
|
-
setItem: (
|
|
639
|
-
ctx: ServerFnMiddlewareResult,
|
|
640
|
-
response: StaticCachedResult,
|
|
641
|
-
) => Promise<void>
|
|
642
|
-
fetchItem: (
|
|
643
|
-
ctx: ServerFnMiddlewareResult,
|
|
644
|
-
) => StaticCachedResult | Promise<StaticCachedResult | undefined>
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
export let serverFnStaticCache: ServerFnStaticCache | undefined
|
|
648
|
-
|
|
649
|
-
export function setServerFnStaticCache(
|
|
650
|
-
cache?: ServerFnStaticCache | (() => ServerFnStaticCache | undefined),
|
|
651
|
-
) {
|
|
652
|
-
const previousCache = serverFnStaticCache
|
|
653
|
-
serverFnStaticCache = typeof cache === 'function' ? cache() : cache
|
|
654
|
-
|
|
655
|
-
return () => {
|
|
656
|
-
serverFnStaticCache = previousCache
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
export function createServerFnStaticCache(
|
|
661
|
-
serverFnStaticCache: ServerFnStaticCache,
|
|
662
|
-
) {
|
|
663
|
-
return serverFnStaticCache
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
/**
|
|
667
|
-
* This is a simple hash function for generating a hash from a string to make the filenames shorter.
|
|
668
|
-
*
|
|
669
|
-
* It is not cryptographically secure (as its using SHA-1) and should not be used for any security purposes.
|
|
670
|
-
*
|
|
671
|
-
* It is only used to generate a hash for the static cache filenames.
|
|
672
|
-
*
|
|
673
|
-
* @param message - The input string to hash.
|
|
674
|
-
* @returns A promise that resolves to the SHA-1 hash of the input string in hexadecimal format.
|
|
675
|
-
*
|
|
676
|
-
* @example
|
|
677
|
-
* ```typescript
|
|
678
|
-
* const hash = await sha1Hash("hello");
|
|
679
|
-
* console.log(hash); // Outputs the SHA-1 hash of "hello" -> "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d"
|
|
680
|
-
* ```
|
|
681
|
-
*/
|
|
682
|
-
async function sha1Hash(message: string): Promise<string> {
|
|
683
|
-
// Encode the string as UTF-8
|
|
684
|
-
const msgBuffer = new TextEncoder().encode(message)
|
|
685
|
-
|
|
686
|
-
// Hash the message
|
|
687
|
-
const hashBuffer = await crypto.subtle.digest('SHA-1', msgBuffer)
|
|
688
|
-
|
|
689
|
-
// Convert the ArrayBuffer to a string
|
|
690
|
-
const hashArray = Array.from(new Uint8Array(hashBuffer))
|
|
691
|
-
const hashHex = hashArray.map((b) => b.toString(16).padStart(2, '0')).join('')
|
|
692
|
-
return hashHex
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
setServerFnStaticCache(() => {
|
|
696
|
-
const getStaticCacheUrl = async (
|
|
697
|
-
options: ServerFnMiddlewareResult,
|
|
698
|
-
hash: string,
|
|
699
|
-
) => {
|
|
700
|
-
const filename = await sha1Hash(`${options.functionId}__${hash}`)
|
|
701
|
-
return `/__tsr/staticServerFnCache/${filename}.json`
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
const jsonToFilenameSafeString = (json: any) => {
|
|
705
|
-
// Custom replacer to sort keys
|
|
706
|
-
const sortedKeysReplacer = (key: string, value: any) =>
|
|
707
|
-
value && typeof value === 'object' && !Array.isArray(value)
|
|
708
|
-
? Object.keys(value)
|
|
709
|
-
.sort()
|
|
710
|
-
.reduce((acc: any, curr: string) => {
|
|
711
|
-
acc[curr] = value[curr]
|
|
712
|
-
return acc
|
|
713
|
-
}, {})
|
|
714
|
-
: value
|
|
715
|
-
|
|
716
|
-
// Convert JSON to string with sorted keys
|
|
717
|
-
const jsonString = JSON.stringify(json ?? '', sortedKeysReplacer)
|
|
718
|
-
|
|
719
|
-
// Replace characters invalid in filenames
|
|
720
|
-
return jsonString
|
|
721
|
-
.replace(/[/\\?%*:|"<>]/g, '-') // Replace invalid characters with a dash
|
|
722
|
-
.replace(/\s+/g, '_') // Optionally replace whitespace with underscores
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
const staticClientCache =
|
|
726
|
-
typeof document !== 'undefined' ? new Map<string, any>() : null
|
|
727
|
-
|
|
728
|
-
return createServerFnStaticCache({
|
|
729
|
-
getItem: async (ctx) => {
|
|
730
|
-
if (typeof document === 'undefined') {
|
|
731
|
-
const hash = jsonToFilenameSafeString(ctx.data)
|
|
732
|
-
const url = await getStaticCacheUrl(ctx, hash)
|
|
733
|
-
const publicUrl = process.env.TSS_OUTPUT_PUBLIC_DIR!
|
|
734
|
-
|
|
735
|
-
// Use fs instead of fetch to read from filesystem
|
|
736
|
-
const { promises: fs } = await import('node:fs')
|
|
737
|
-
const path = await import('node:path')
|
|
738
|
-
const filePath = path.join(publicUrl, url)
|
|
739
|
-
|
|
740
|
-
const [cachedResult, readError] = await fs
|
|
741
|
-
.readFile(filePath, 'utf-8')
|
|
742
|
-
.then((c) => [
|
|
743
|
-
startSerializer.parse(c) as {
|
|
744
|
-
ctx: unknown
|
|
745
|
-
error: any
|
|
746
|
-
},
|
|
747
|
-
null,
|
|
748
|
-
])
|
|
749
|
-
.catch((e) => [null, e])
|
|
750
|
-
|
|
751
|
-
if (readError && readError.code !== 'ENOENT') {
|
|
752
|
-
throw readError
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
return cachedResult as StaticCachedResult
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
return undefined
|
|
759
|
-
},
|
|
760
|
-
setItem: async (ctx, response) => {
|
|
761
|
-
const { promises: fs } = await import('node:fs')
|
|
762
|
-
const path = await import('node:path')
|
|
763
|
-
|
|
764
|
-
const hash = jsonToFilenameSafeString(ctx.data)
|
|
765
|
-
const url = await getStaticCacheUrl(ctx, hash)
|
|
766
|
-
const publicUrl = process.env.TSS_OUTPUT_PUBLIC_DIR!
|
|
767
|
-
const filePath = path.join(publicUrl, url)
|
|
768
|
-
|
|
769
|
-
// Ensure the directory exists
|
|
770
|
-
await fs.mkdir(path.dirname(filePath), { recursive: true })
|
|
771
|
-
|
|
772
|
-
// Store the result with fs
|
|
773
|
-
await fs.writeFile(filePath, startSerializer.stringify(response))
|
|
774
|
-
},
|
|
775
|
-
fetchItem: async (ctx) => {
|
|
776
|
-
const hash = jsonToFilenameSafeString(ctx.data)
|
|
777
|
-
const url = await getStaticCacheUrl(ctx, hash)
|
|
778
|
-
|
|
779
|
-
let result: any = staticClientCache?.get(url)
|
|
780
|
-
|
|
781
|
-
if (!result) {
|
|
782
|
-
result = await fetch(url, {
|
|
783
|
-
method: 'GET',
|
|
784
|
-
})
|
|
785
|
-
.then((r) => r.text())
|
|
786
|
-
.then((d) => startSerializer.parse(d))
|
|
787
|
-
|
|
788
|
-
staticClientCache?.set(url, result)
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
return result
|
|
792
|
-
},
|
|
793
|
-
})
|
|
794
|
-
})
|
|
795
|
-
|
|
796
524
|
export function extractFormDataContext(formData: FormData) {
|
|
797
525
|
const serializedContext = formData.get('__TSR_CONTEXT')
|
|
798
526
|
formData.delete('__TSR_CONTEXT')
|
|
@@ -849,14 +577,13 @@ export type ServerFnMiddlewareOptions = {
|
|
|
849
577
|
signal?: AbortSignal
|
|
850
578
|
sendContext?: any
|
|
851
579
|
context?: any
|
|
852
|
-
type: ServerFnTypeOrTypeFn<any, any, any, any>
|
|
853
580
|
functionId: string
|
|
581
|
+
router?: AnyRouter
|
|
854
582
|
}
|
|
855
583
|
|
|
856
584
|
export type ServerFnMiddlewareResult = ServerFnMiddlewareOptions & {
|
|
857
585
|
result?: unknown
|
|
858
586
|
error?: unknown
|
|
859
|
-
type: ServerFnTypeOrTypeFn<any, any, any, any>
|
|
860
587
|
}
|
|
861
588
|
|
|
862
589
|
export type NextFn = (
|
|
@@ -946,35 +673,8 @@ export function serverFnBaseToMiddleware(
|
|
|
946
673
|
...ctx,
|
|
947
674
|
// switch the sendContext over to context
|
|
948
675
|
context: sendContext,
|
|
949
|
-
type: typeof ctx.type === 'function' ? ctx.type(ctx) : ctx.type,
|
|
950
676
|
} as any
|
|
951
677
|
|
|
952
|
-
if (
|
|
953
|
-
ctx.type === 'static' &&
|
|
954
|
-
process.env.NODE_ENV === 'production' &&
|
|
955
|
-
typeof document !== 'undefined'
|
|
956
|
-
) {
|
|
957
|
-
invariant(
|
|
958
|
-
serverFnStaticCache,
|
|
959
|
-
'serverFnStaticCache.fetchItem is not available!',
|
|
960
|
-
)
|
|
961
|
-
|
|
962
|
-
const result = await serverFnStaticCache.fetchItem(payload)
|
|
963
|
-
|
|
964
|
-
if (result) {
|
|
965
|
-
if (result.error) {
|
|
966
|
-
throw result.error
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
return next(result.ctx)
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
warning(
|
|
973
|
-
result,
|
|
974
|
-
`No static cache item found for ${payload.functionId}__${JSON.stringify(payload.data)}, falling back to server function...`,
|
|
975
|
-
)
|
|
976
|
-
}
|
|
977
|
-
|
|
978
678
|
// Execute the extracted function
|
|
979
679
|
// but not before serializing the context
|
|
980
680
|
const res = await options.extractedFn?.(payload)
|
package/src/index.tsx
CHANGED
|
@@ -1,16 +1,22 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export type {
|
|
2
|
+
DehydratedRouter,
|
|
3
|
+
JsonResponse,
|
|
4
|
+
} from '@tanstack/router-core/ssr/client'
|
|
5
|
+
|
|
6
|
+
export { hydrate, json, mergeHeaders } from '@tanstack/router-core/ssr/client'
|
|
7
|
+
|
|
2
8
|
export { startSerializer } from './serializer'
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
|
|
10
|
+
export type {
|
|
11
|
+
StartSerializer,
|
|
12
|
+
Serializable,
|
|
13
|
+
SerializerParse,
|
|
14
|
+
SerializerParseBy,
|
|
15
|
+
SerializerStringify,
|
|
16
|
+
SerializerStringifyBy,
|
|
17
|
+
SerializerExtensions,
|
|
18
|
+
} from './serializer'
|
|
19
|
+
|
|
14
20
|
export {
|
|
15
21
|
createIsomorphicFn,
|
|
16
22
|
type IsomorphicFn,
|
|
@@ -19,8 +25,7 @@ export {
|
|
|
19
25
|
type IsomorphicFnBase,
|
|
20
26
|
} from './createIsomorphicFn'
|
|
21
27
|
export { serverOnly, clientOnly } from './envOnly'
|
|
22
|
-
export {
|
|
23
|
-
export { json } from './json'
|
|
28
|
+
export { createServerFn } from './createServerFn'
|
|
24
29
|
export {
|
|
25
30
|
createMiddleware,
|
|
26
31
|
type IntersectAllValidatorInputs,
|
|
@@ -55,8 +60,6 @@ export {
|
|
|
55
60
|
globalMiddleware,
|
|
56
61
|
} from './registerGlobalMiddleware'
|
|
57
62
|
export type {
|
|
58
|
-
ServerFn as FetchFn,
|
|
59
|
-
ServerFnCtx as FetchFnCtx,
|
|
60
63
|
CompiledFetcherFnOptions,
|
|
61
64
|
CompiledFetcherFn,
|
|
62
65
|
Fetcher,
|
|
@@ -70,11 +73,9 @@ export type {
|
|
|
70
73
|
ServerFnMiddlewareOptions,
|
|
71
74
|
ServerFnMiddlewareResult,
|
|
72
75
|
ServerFnBuilder,
|
|
73
|
-
ServerFnType,
|
|
74
76
|
ServerFnBaseOptions,
|
|
75
77
|
NextFn,
|
|
76
78
|
Method,
|
|
77
|
-
StaticCachedResult,
|
|
78
79
|
OptionalFetcher,
|
|
79
80
|
RequiredFetcher,
|
|
80
81
|
} from './createServerFn'
|
|
@@ -84,6 +85,5 @@ export {
|
|
|
84
85
|
serverFnBaseToMiddleware,
|
|
85
86
|
extractFormDataContext,
|
|
86
87
|
flattenMiddlewares,
|
|
87
|
-
serverFnStaticCache,
|
|
88
88
|
executeMiddleware,
|
|
89
89
|
} from './createServerFn'
|
package/src/serializer.ts
CHANGED
|
@@ -1,6 +1,37 @@
|
|
|
1
1
|
import { isPlainObject } from '@tanstack/router-core'
|
|
2
|
-
import type { StartSerializer } from '@tanstack/router-core'
|
|
3
2
|
|
|
3
|
+
export interface StartSerializer {
|
|
4
|
+
stringify: (obj: unknown) => string
|
|
5
|
+
parse: (str: string) => unknown
|
|
6
|
+
encode: <T>(value: T) => T
|
|
7
|
+
decode: <T>(value: T) => T
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type SerializerStringifyBy<T, TSerializable> = T extends TSerializable
|
|
11
|
+
? T
|
|
12
|
+
: T extends (...args: Array<any>) => any
|
|
13
|
+
? 'Function is not serializable'
|
|
14
|
+
: { [K in keyof T]: SerializerStringifyBy<T[K], TSerializable> }
|
|
15
|
+
|
|
16
|
+
export type SerializerParseBy<T, TSerializable> = T extends TSerializable
|
|
17
|
+
? T
|
|
18
|
+
: unknown extends SerializerExtensions['ReadableStream']
|
|
19
|
+
? { [K in keyof T]: SerializerParseBy<T[K], TSerializable> }
|
|
20
|
+
: T extends SerializerExtensions['ReadableStream']
|
|
21
|
+
? ReadableStream
|
|
22
|
+
: { [K in keyof T]: SerializerParseBy<T[K], TSerializable> }
|
|
23
|
+
|
|
24
|
+
export interface DefaultSerializerExtensions {
|
|
25
|
+
ReadableStream: unknown
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface SerializerExtensions extends DefaultSerializerExtensions {}
|
|
29
|
+
|
|
30
|
+
export type Serializable = Date | undefined | Error | FormData | bigint
|
|
31
|
+
|
|
32
|
+
export type SerializerStringify<T> = SerializerStringifyBy<T, Serializable>
|
|
33
|
+
|
|
34
|
+
export type SerializerParse<T> = SerializerParseBy<T, Serializable>
|
|
4
35
|
export const startSerializer: StartSerializer = {
|
|
5
36
|
stringify: (value: any) =>
|
|
6
37
|
JSON.stringify(value, function replacer(key, val) {
|
|
@@ -73,7 +104,6 @@ export const startSerializer: StartSerializer = {
|
|
|
73
104
|
return value
|
|
74
105
|
},
|
|
75
106
|
}
|
|
76
|
-
|
|
77
107
|
const createSerializer = <TKey extends string, TInput, TSerialized>(
|
|
78
108
|
key: TKey,
|
|
79
109
|
check: (value: any) => value is TInput,
|
|
@@ -86,7 +116,6 @@ const createSerializer = <TKey extends string, TInput, TSerialized>(
|
|
|
86
116
|
parseCondition: (value: any) => Object.hasOwn(value, `$${key}`),
|
|
87
117
|
parse: (value: any) => fromValue(value[`$${key}`]),
|
|
88
118
|
})
|
|
89
|
-
|
|
90
119
|
// Keep these ordered by predicted frequency
|
|
91
120
|
// Make sure to keep DefaultSerializable in sync with these serializers
|
|
92
121
|
// Also, make sure that they are unit tested in serializer.test.tsx
|
|
@@ -52,7 +52,6 @@ test('createServerFn with validator', () => {
|
|
|
52
52
|
expectTypeOf(fn).parameter(0).toEqualTypeOf<{
|
|
53
53
|
data: { input: string }
|
|
54
54
|
headers?: HeadersInit
|
|
55
|
-
type?: 'static' | 'dynamic'
|
|
56
55
|
signal?: AbortSignal
|
|
57
56
|
}>()
|
|
58
57
|
|
|
@@ -168,7 +167,6 @@ describe('createServerFn with middleware and validator', () => {
|
|
|
168
167
|
readonly inputC: 'inputC'
|
|
169
168
|
}
|
|
170
169
|
headers?: HeadersInit
|
|
171
|
-
type?: 'static' | 'dynamic'
|
|
172
170
|
signal?: AbortSignal
|
|
173
171
|
}>()
|
|
174
172
|
|
|
@@ -212,7 +210,6 @@ describe('createServerFn with middleware and validator', () => {
|
|
|
212
210
|
readonly inputC: 'inputC'
|
|
213
211
|
}
|
|
214
212
|
headers?: HeadersInit
|
|
215
|
-
type?: 'static' | 'dynamic'
|
|
216
213
|
signal?: AbortSignal
|
|
217
214
|
}>()
|
|
218
215
|
|
|
@@ -323,7 +320,6 @@ test('createServerFn where validator is optional if object is optional', () => {
|
|
|
323
320
|
| {
|
|
324
321
|
data?: 'c' | undefined
|
|
325
322
|
headers?: HeadersInit
|
|
326
|
-
type?: 'static' | 'dynamic'
|
|
327
323
|
signal?: AbortSignal
|
|
328
324
|
}
|
|
329
325
|
| undefined
|
|
@@ -347,7 +343,6 @@ test('createServerFn where data is optional if there is no validator', () => {
|
|
|
347
343
|
| {
|
|
348
344
|
data?: undefined
|
|
349
345
|
headers?: HeadersInit
|
|
350
|
-
type?: 'static' | 'dynamic'
|
|
351
346
|
signal?: AbortSignal
|
|
352
347
|
}
|
|
353
348
|
| undefined
|
|
@@ -509,7 +504,6 @@ test('createServerFn validator infers unknown for default input type', () => {
|
|
|
509
504
|
| {
|
|
510
505
|
data?: unknown | undefined
|
|
511
506
|
headers?: HeadersInit
|
|
512
|
-
type?: 'static' | 'dynamic'
|
|
513
507
|
signal?: AbortSignal
|
|
514
508
|
}
|
|
515
509
|
| undefined
|