Skip to content

Commit 6949dae

Browse files
schiller-manueltannerlinsley
authored andcommitted
use seroval for serverfunctions
1 parent 21a7da5 commit 6949dae

32 files changed

+598
-780
lines changed

e2e/react-start/server-functions/tests/server-functions.spec.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,11 @@ test('invoking a server function with custom response status code', async ({
1414
await page.waitForLoadState('networkidle')
1515

1616
const requestPromise = new Promise<void>((resolve) => {
17-
page.on('response', async (response) => {
17+
page.on('response', (response) => {
1818
expect(response.status()).toBe(225)
1919
expect(response.statusText()).toBe('hello')
20-
expect(response.headers()['content-type']).toBe('application/json')
21-
expect(await response.json()).toEqual(
22-
expect.objectContaining({
23-
result: { hello: 'world' },
24-
context: {},
25-
}),
20+
expect(response.headers()['content-type']).toContain(
21+
'application/x-ndjson',
2622
)
2723
resolve()
2824
})

e2e/solid-start/server-functions/tests/server-functions.spec.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,11 @@ test('invoking a server function with custom response status code', async ({
1212
await page.waitForLoadState('networkidle')
1313

1414
const requestPromise = new Promise<void>((resolve) => {
15-
page.on('response', async (response) => {
15+
page.on('response', (response) => {
1616
expect(response.status()).toBe(225)
1717
expect(response.statusText()).toBe('hello')
18-
expect(response.headers()['content-type']).toBe('application/json')
19-
expect(await response.json()).toEqual(
20-
expect.objectContaining({
21-
result: { hello: 'world' },
22-
context: {},
23-
}),
18+
expect(response.headers()['content-type']).toContain(
19+
'application/x-ndjson',
2420
)
2521
resolve()
2622
})

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
export {
22
mergeHeaders,
3-
startSerializer,
43
createIsomorphicFn,
54
createServerFn,
65
createMiddleware,

packages/router-core/src/index.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,12 @@ export type {
412412
ValidateUseParamsResult,
413413
} from './typePrimitives'
414414

415-
export type { AnyTransformer, Transformer } from './ssr/transformer'
415+
export type { AnyTransformer, Transformer } from './ssr/serializer/transformer'
416416

417-
export { createSerializationAdapter } from './ssr/transformer'
417+
export {
418+
createSerializationAdapter,
419+
makeSerovalPlugin,
420+
makeSsrSerovalPlugin,
421+
} from './ssr/serializer/transformer'
422+
423+
export { defaultSerovalPlugins } from './ssr/serializer/seroval-plugins'

packages/router-core/src/ssr/seroval-plugins.ts renamed to packages/router-core/src/ssr/serializer/ShallowErrorPlugin.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { createPlugin } from 'seroval'
22
import type { SerovalNode } from 'seroval'
33

4-
interface ErrorNode {
4+
export interface ErrorNode {
55
message: SerovalNode
66
}
77

@@ -13,7 +13,7 @@ export const ShallowErrorPlugin = /* @__PURE__ */ createPlugin<
1313
Error,
1414
ErrorNode
1515
>({
16-
tag: 'tanstack-router:seroval-plugins/Error',
16+
tag: '$TSR/Error',
1717
test(value) {
1818
return value instanceof Error
1919
},
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { ReadableStreamPlugin } from 'seroval-plugins/web'
2+
import { ShallowErrorPlugin } from './ShallowErrorPlugin'
3+
import type { Plugin } from 'seroval'
4+
5+
export const defaultSerovalPlugins = [
6+
ShallowErrorPlugin as Plugin<Error, any>,
7+
// ReadableStreamNode is not exported by seroval
8+
ReadableStreamPlugin as Plugin<ReadableStream, any>,
9+
]
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { createPlugin } from 'seroval'
2+
import { GLOBAL_TSR } from '../constants'
3+
import type { SerovalNode } from 'seroval'
4+
5+
export type Transformer<TInput, TTransformed> = {
6+
key: string
7+
test: (value: any) => value is TInput
8+
toSerializable: (value: TInput) => TTransformed
9+
fromSerializable: (value: TTransformed) => TInput
10+
}
11+
12+
export type AnyTransformer = Transformer<any, any>
13+
14+
export function createSerializationAdapter<
15+
TKey extends string,
16+
TInput,
17+
TTransformed /* we need to check that this type is actually serializable taking into account all seroval native types and any custom plugin WE=router/start add!!! */,
18+
>(opts: {
19+
key: TKey
20+
test: (value: any) => value is TInput
21+
toSerializable: (value: TInput) => TTransformed
22+
fromSerializable: (value: TTransformed) => TInput
23+
}): Transformer<TInput, TTransformed> {
24+
return opts
25+
}
26+
27+
export function makeSsrSerovalPlugin<TInput, TTransformed>(
28+
transformer: Transformer<TInput, TTransformed>,
29+
options: { didRun: boolean },
30+
) {
31+
return createPlugin<TInput, SerovalNode>({
32+
tag: '$TSR/t/' + transformer.key,
33+
test: transformer.test,
34+
parse: {
35+
stream(value, ctx) {
36+
return ctx.parse(transformer.toSerializable(value))
37+
},
38+
},
39+
serialize(node, ctx) {
40+
options.didRun = true
41+
return (
42+
GLOBAL_TSR +
43+
'.t.get("' +
44+
transformer.key +
45+
'")(' +
46+
ctx.serialize(node) +
47+
')'
48+
)
49+
},
50+
// we never deserialize on the server during SSR
51+
deserialize: undefined as never,
52+
})
53+
}
54+
55+
export function makeSerovalPlugin<TInput, TTransformed>(
56+
transformer: Transformer<TInput, TTransformed>,
57+
) {
58+
return createPlugin<TInput, SerovalNode>({
59+
tag: '$TSR/t/' + transformer.key,
60+
test: transformer.test,
61+
parse: {
62+
sync(value, ctx) {
63+
return ctx.parse(transformer.toSerializable(value))
64+
},
65+
async async(value, ctx) {
66+
return await ctx.parse(transformer.toSerializable(value))
67+
},
68+
stream(value, ctx) {
69+
return ctx.parse(transformer.toSerializable(value))
70+
},
71+
},
72+
// we don't generate JS code outside of SSR (for now)
73+
serialize: undefined as never,
74+
deserialize(node, ctx) {
75+
return transformer.fromSerializable(ctx.deserialize(node) as TTransformed)
76+
},
77+
})
78+
}

packages/router-core/src/ssr/ssr-client.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import type { AnyRouteMatch, MakeRouteMatch } from '../Matches'
55
import type { AnyRouter } from '../router'
66
import type { Manifest } from '../manifest'
77
import type { RouteContextOptions } from '../route'
8-
import type { AnyTransformer } from './transformer'
8+
import type { AnyTransformer } from './serializer/transformer'
99
import type { GLOBAL_TSR } from './constants'
1010

1111
declare global {

packages/router-core/src/ssr/ssr-server.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
import { crossSerializeStream, getCrossReferenceHeader } from 'seroval'
2-
import { ReadableStreamPlugin } from 'seroval-plugins/web'
32
import invariant from 'tiny-invariant'
43
import { createControlledPromise } from '../utils'
54
import minifiedTsrBootStrapScript from './tsrScript?script-string'
6-
import { ShallowErrorPlugin } from './seroval-plugins'
75
import { GLOBAL_TSR } from './constants'
6+
import { defaultSerovalPlugins } from './serializer/seroval-plugins'
7+
import { makeSsrSerovalPlugin } from './serializer/transformer'
88
import type { AnyRouter } from '../router'
99
import type { DehydratedMatch } from './ssr-client'
1010
import type { DehydratedRouter } from './client'
1111
import type { AnyRouteMatch } from '../Matches'
1212
import type { Manifest } from '../manifest'
13-
import type { AnyTransformer } from './transformer'
13+
import type { AnyTransformer } from './serializer/transformer'
1414

1515
declare module '../router' {
1616
interface ServerSsr {
@@ -55,8 +55,6 @@ export function attachRouterServerSsrUtils(
5555
router.ssr = {
5656
manifest,
5757
}
58-
const serializationRefs = new Map<unknown, number>()
59-
6058
let initialScriptSent = false
6159
const getInitialScript = () => {
6260
if (initialScriptSent) {
@@ -113,10 +111,10 @@ export function attachRouterServerSsrUtils(
113111
router.options.serializationAdapters as
114112
| Array<AnyTransformer>
115113
| undefined
116-
)?.map((t) => t.makePlugin(trackPlugins)) ?? []
114+
)?.map((t) => makeSsrSerovalPlugin(t, trackPlugins)) ?? []
117115
crossSerializeStream(dehydratedRouter, {
118-
refs: serializationRefs,
119-
plugins: [...plugins, ReadableStreamPlugin, ShallowErrorPlugin],
116+
refs: new Map(),
117+
plugins: [...plugins, ...defaultSerovalPlugins],
120118
onSerialize: (data, initial) => {
121119
let serialized = initial ? GLOBAL_TSR + '.router=' + data : data
122120
if (trackPlugins.didRun) {

packages/router-core/src/ssr/transformer.ts

Lines changed: 0 additions & 60 deletions
This file was deleted.

0 commit comments

Comments
 (0)