Skip to content

Commit c6cd8c3

Browse files
authored
fix: correctly deserialize on the server (#13686)
fixes #13675 This PR adds an optional chain for when deserialize tries to access app.decoders since app is always undefined on the server and is only initialised on the client.
1 parent 1a40675 commit c6cd8c3

File tree

8 files changed

+62
-7
lines changed

8 files changed

+62
-7
lines changed

.changeset/silly-berries-repair.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/kit': patch
3+
---
4+
5+
fix: correctly run `deserialize` on the server

packages/kit/src/runtime/app/forms.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import * as devalue from 'devalue';
2-
import { DEV } from 'esm-env';
2+
import { BROWSER, DEV } from 'esm-env';
33
import { invalidateAll } from './navigation.js';
4-
import { app, applyAction } from '../client/client.js';
4+
import { app as client_app, applyAction } from '../client/client.js';
5+
import { app as server_app } from '../server/app.js';
56

67
export { applyAction };
78

9+
const decoders = BROWSER ? client_app.decoders : server_app?.decoders;
10+
811
/**
912
* Use this function to deserialize the response from a form submission.
1013
* Usage:
@@ -31,7 +34,7 @@ export function deserialize(result) {
3134
const parsed = JSON.parse(result);
3235

3336
if (parsed.data) {
34-
parsed.data = devalue.parse(parsed.data, app.decoders);
37+
parsed.data = devalue.parse(parsed.data, decoders);
3538
}
3639

3740
return parsed;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/** @type {{ decoders: Record<string, (data: any) => any> }} */
2+
export let app;
3+
4+
/**
5+
* @param {{ decoders: Record<string, (data: any) => any> }} value
6+
*/
7+
export function set_app(value) {
8+
app = value;
9+
}

packages/kit/src/runtime/server/index.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { DEV } from 'esm-env';
55
import { filter_private_env, filter_public_env } from '../../utils/env.js';
66
import { prerendering } from '__sveltekit/environment';
77
import { set_read_implementation, set_manifest } from '__sveltekit/server';
8+
import { set_app } from './app.js';
89

910
/** @type {ProxyHandler<{ type: 'public' | 'private' }>} */
1011
const prerender_env_handler = {
@@ -83,6 +84,12 @@ export class Server {
8384
transport: module.transport || {}
8485
};
8586

87+
set_app({
88+
decoders: module.transport
89+
? Object.fromEntries(Object.entries(module.transport).map(([k, v]) => [k, v.decode]))
90+
: {}
91+
});
92+
8693
if (module.init) {
8794
await module.init();
8895
}
@@ -97,6 +104,10 @@ export class Server {
97104
reroute: () => {},
98105
transport: {}
99106
};
107+
108+
set_app({
109+
decoders: {}
110+
});
100111
} else {
101112
throw error;
102113
}

packages/kit/test/apps/basics/src/routes/serialization-form/+page.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script lang="ts">
2-
let { form } = $props();
2+
const { form } = $props();
33
</script>
44

55
<form method="POST">
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { deserialize } from '$app/forms';
2+
3+
export async function GET({ fetch }) {
4+
const response = await fetch('/serialization-form', {
5+
method: 'POST',
6+
body: new FormData(),
7+
headers: {
8+
'x-sveltekit-action': 'true'
9+
}
10+
});
11+
const result = deserialize(await response.text());
12+
13+
if (result.type === 'success' && result.data) {
14+
return Response.json({
15+
data: /** @type {import('../../../lib').Foo} */ (result.data.foo).bar()
16+
});
17+
}
18+
19+
return Response.json({});
20+
}

packages/kit/test/apps/basics/src/routes/serialization-form2/+page.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script lang="ts">
22
import { enhance } from '$app/forms';
33
4-
let { form } = $props();
4+
const { form } = $props();
55
</script>
66

77
<form method="POST" use:enhance>

packages/kit/test/apps/basics/test/server.test.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -639,7 +639,7 @@ test.describe('Static files', () => {
639639
test.describe('setHeaders', () => {
640640
test('allows multiple set-cookie headers with different values', async ({ page }) => {
641641
const response = await page.goto('/headers/set-cookie/sub');
642-
const cookies = (await response.allHeaders())['set-cookie'];
642+
const cookies = response ? (await response.allHeaders())['set-cookie'] : '';
643643

644644
expect(cookies).toMatch('cookie1=value1');
645645
expect(cookies).toMatch('cookie2=value2');
@@ -649,7 +649,7 @@ test.describe('setHeaders', () => {
649649
test.describe('cookies', () => {
650650
test('cookie.serialize created correct cookie header string', async ({ page }) => {
651651
const response = await page.goto('/cookies/serialize');
652-
const cookies = await response.headerValue('set-cookie');
652+
const cookies = response ? await response.headerValue('set-cookie') : '';
653653

654654
expect(cookies).toMatch('before=before');
655655
expect(cookies).toMatch('after=after');
@@ -729,3 +729,10 @@ test.describe('getRequestEvent', () => {
729729
expect(await response.text()).toBe('hello from hooks.server.js');
730730
});
731731
});
732+
733+
test.describe('$app/forms', () => {
734+
test('deserialize works on the server', async ({ request }) => {
735+
const response = await request.get('/serialization-form/server-deserialize');
736+
expect(await response.json()).toEqual({ data: 'It works!' });
737+
});
738+
});

0 commit comments

Comments
 (0)