Skip to content

Commit f6f0452

Browse files
feat(2.0): seroval json mode (#2042)
Co-authored-by: Atila Fassina <atila@fassina.eu>
1 parent 5e17472 commit f6f0452

File tree

13 files changed

+611
-283
lines changed

13 files changed

+611
-283
lines changed

.changeset/plenty-geese-enter.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@solidjs/start": minor
3+
---
4+
5+
seroval json mode

.vscode/settings.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,11 @@
44
},
55
"typescript.tsdk": "node_modules/typescript/lib",
66
"editor.formatOnSave": true,
7-
"editor.defaultFormatter": "oxc.oxc-vscode"
7+
"editor.defaultFormatter": "oxc.oxc-vscode",
8+
"[typescript]": {
9+
"editor.defaultFormatter": "oxc.oxc-vscode"
10+
},
11+
"[typescriptreact]": {
12+
"editor.defaultFormatter": "oxc.oxc-vscode"
13+
}
814
}

apps/tests/src/e2e/server-function.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,18 @@ test.describe("server-function", () => {
6767
await page.goto("http://localhost:3000/generator-server-function");
6868
await expect(page.locator("#server-fn-test")).toContainText("¡Hola, Mundo!");
6969
});
70+
71+
test("should build with a server function ping", async ({ page }) => {
72+
await page.goto("http://localhost:3000/server-function-ping");
73+
await expect(page.locator("#server-fn-test")).toContainText('{"result":true}');
74+
});
75+
76+
test("should build with a server function w/ form data", async ({ page }) => {
77+
await page.goto("http://localhost:3000/server-function-form-data");
78+
await expect(page.locator("#server-fn-test")).toContainText('{"result":true}');
79+
});
80+
test("should build with a server function w/ blob data", async ({ page }) => {
81+
await page.goto("http://localhost:3000/server-function-blob");
82+
await expect(page.locator("#server-fn-test")).toContainText('{"result":true}');
83+
});
7084
});
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { createEffect, createSignal } from "solid-js";
2+
3+
async function ping(value: Blob) {
4+
"use server";
5+
return value;
6+
}
7+
8+
const blobURI =
9+
"data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCAxNjYgMTU1LjMnPjxwYXRoIGQ9J00xNjMgMzVTMTEwLTQgNjkgNWwtMyAxYy02IDItMTEgNS0xNCA5bC0yIDMtMTUgMjYgMjYgNWMxMSA3IDI1IDEwIDM4IDdsNDYgOSAxOC0zMHonIGZpbGw9JyM3NmIzZTEnLz48bGluZWFyR3JhZGllbnQgaWQ9J2EnIGdyYWRpZW50VW5pdHM9J3VzZXJTcGFjZU9uVXNlJyB4MT0nMjcuNScgeTE9JzMnIHgyPScxNTInIHkyPSc2My41Jz48c3RvcCBvZmZzZXQ9Jy4xJyBzdG9wLWNvbG9yPScjNzZiM2UxJy8+PHN0b3Agb2Zmc2V0PScuMycgc3RvcC1jb2xvcj0nI2RjZjJmZCcvPjxzdG9wIG9mZnNldD0nMScgc3RvcC1jb2xvcj0nIzc2YjNlMScvPjwvbGluZWFyR3JhZGllbnQ+PHBhdGggZD0nTTE2MyAzNVMxMTAtNCA2OSA1bC0zIDFjLTYgMi0xMSA1LTE0IDlsLTIgMy0xNSAyNiAyNiA1YzExIDcgMjUgMTAgMzggN2w0NiA5IDE4LTMweicgb3BhY2l0eT0nLjMnIGZpbGw9J3VybCgjYSknLz48cGF0aCBkPSdNNTIgMzVsLTQgMWMtMTcgNS0yMiAyMS0xMyAzNSAxMCAxMyAzMSAyMCA0OCAxNWw2Mi0yMVM5MiAyNiA1MiAzNXonIGZpbGw9JyM1MThhYzgnLz48bGluZWFyR3JhZGllbnQgaWQ9J2InIGdyYWRpZW50VW5pdHM9J3VzZXJTcGFjZU9uVXNlJyB4MT0nOTUuOCcgeTE9JzMyLjYnIHgyPSc3NCcgeTI9JzEwNS4yJz48c3RvcCBvZmZzZXQ9JzAnIHN0b3AtY29sb3I9JyM3NmIzZTEnLz48c3RvcCBvZmZzZXQ9Jy41JyBzdG9wLWNvbG9yPScjNDM3N2JiJy8+PHN0b3Agb2Zmc2V0PScxJyBzdG9wLWNvbG9yPScjMWYzYjc3Jy8+PC9saW5lYXJHcmFkaWVudD48cGF0aCBkPSdNNTIgMzVsLTQgMWMtMTcgNS0yMiAyMS0xMyAzNSAxMCAxMyAzMSAyMCA0OCAxNWw2Mi0yMVM5MiAyNiA1MiAzNXonIG9wYWNpdHk9Jy4zJyBmaWxsPSd1cmwoI2IpJy8+PGxpbmVhckdyYWRpZW50IGlkPSdjJyBncmFkaWVudFVuaXRzPSd1c2VyU3BhY2VPblVzZScgeDE9JzE4LjQnIHkxPSc2NC4yJyB4Mj0nMTQ0LjMnIHkyPScxNDkuOCc+PHN0b3Agb2Zmc2V0PScwJyBzdG9wLWNvbG9yPScjMzE1YWE5Jy8+PHN0b3Agb2Zmc2V0PScuNScgc3RvcC1jb2xvcj0nIzUxOGFjOCcvPjxzdG9wIG9mZnNldD0nMScgc3RvcC1jb2xvcj0nIzMxNWFhOScvPjwvbGluZWFyR3JhZGllbnQ+PHBhdGggZD0nTTEzNCA4MGE0NSA0NSAwIDAwLTQ4LTE1TDI0IDg1IDQgMTIwbDExMiAxOSAyMC0zNmM0LTcgMy0xNS0yLTIzeicgZmlsbD0ndXJsKCNjKScvPjxsaW5lYXJHcmFkaWVudCBpZD0nZCcgZ3JhZGllbnRVbml0cz0ndXNlclNwYWNlT25Vc2UnIHgxPSc3NS4yJyB5MT0nNzQuNScgeDI9JzI0LjQnIHkyPScyNjAuOCc+PHN0b3Agb2Zmc2V0PScwJyBzdG9wLWNvbG9yPScjNDM3N2JiJy8+PHN0b3Agb2Zmc2V0PScuNScgc3RvcC1jb2xvcj0nIzFhMzM2YicvPjxzdG9wIG9mZnNldD0nMScgc3RvcC1jb2xvcj0nIzFhMzM2YicvPjwvbGluZWFyR3JhZGllbnQ+PHBhdGggZD0nTTExNCAxMTVhNDUgNDUgMCAwMC00OC0xNUw0IDEyMHM1MyA0MCA5NCAzMGwzLTFjMTctNSAyMy0yMSAxMy0zNHonIGZpbGw9J3VybCgjZCknLz48L3N2Zz4=";
10+
11+
export default function App() {
12+
const [output, setOutput] = createSignal<{ result?: boolean }>({});
13+
14+
createEffect(async () => {
15+
const request = await fetch(blobURI);
16+
const blob = await request.blob();
17+
const result = await ping(blob);
18+
const value = await blob.text();
19+
const test = await result.text();
20+
21+
setOutput(prev => ({ ...prev, result: value === test }));
22+
});
23+
24+
return (
25+
<main>
26+
<span id="server-fn-test">{JSON.stringify(output())}</span>
27+
</main>
28+
);
29+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { createEffect, createSignal } from "solid-js";
2+
3+
async function ping(value: FormData) {
4+
"use server";
5+
const file = value.get("example") as File;
6+
return await file.text();
7+
}
8+
9+
export default function App() {
10+
const [output, setOutput] = createSignal<{ result?: boolean }>({});
11+
12+
createEffect(async () => {
13+
const file = new File(["Hello, World!"], "hello-world.txt");
14+
const formData = new FormData();
15+
formData.append("example", file);
16+
const result = await ping(formData);
17+
const value = await file.text();
18+
setOutput(prev => ({ ...prev, result: value === result }));
19+
});
20+
21+
return (
22+
<main>
23+
<span id="server-fn-test">{JSON.stringify(output())}</span>
24+
</main>
25+
);
26+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { createEffect, createSignal } from "solid-js";
2+
3+
async function ping(value: string) {
4+
"use server";
5+
6+
return await Promise.resolve(value);
7+
}
8+
9+
export default function App() {
10+
const [output, setOutput] = createSignal<{ result?: boolean }>({});
11+
12+
createEffect(async () => {
13+
const value = `${Math.random() * 1000}`;
14+
const result = await ping(value);
15+
setOutput(prev => ({ ...prev, result: value === result }));
16+
});
17+
18+
return (
19+
<main>
20+
<span id="server-fn-test">{JSON.stringify(output())}</span>
21+
</main>
22+
);
23+
}

packages/start/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@
5656
"path-to-regexp": "^8.2.0",
5757
"pathe": "^2.0.3",
5858
"radix3": "^1.1.2",
59-
"seroval": "^1.4.1",
60-
"seroval-plugins": "^1.4.0",
59+
"seroval": "^1.5.0",
60+
"seroval-plugins": "^1.5.0",
6161
"shiki": "^1.26.1",
6262
"solid-js": "^1.9.9",
6363
"source-map-js": "^1.2.1",

packages/start/src/config/index.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,17 @@ export interface SolidStartOptions {
2121
routeDir?: string;
2222
extensions?: string[];
2323
middleware?: string;
24+
serialization?: {
25+
/**
26+
* The serialization mode to use for server functions/actions.
27+
* The "js" mode uses a custom binary format that is more efficient than JSON, but requires a custom deserializer (with `eval()`) on the client.
28+
* A strong CSP should block `eval()` executions, which would prevent the "js" mode from working.
29+
* The "json" mode uses JSON for serialization, which is less efficient but can be deserialized with `JSON.parse` on the client.
30+
*
31+
* @default "json"
32+
*/
33+
mode?: "js" | "json";
34+
};
2435
}
2536

2637
const absolute = (path: string, root: string) =>
@@ -131,6 +142,7 @@ export function solidStart(options?: SolidStartOptions): Array<PluginOption> {
131142
"import.meta.env.START_APP_ENTRY": JSON.stringify(appEntryPath),
132143
"import.meta.env.START_CLIENT_ENTRY": JSON.stringify(handlers.client),
133144
"import.meta.env.START_DEV_OVERLAY": JSON.stringify(start.devOverlay),
145+
"import.meta.env.SEROVAL_MODE": JSON.stringify(start.serialization?.mode || "json"),
134146
},
135147
builder: {
136148
sharedPlugins: true,
@@ -176,7 +188,7 @@ export function solidStart(options?: SolidStartOptions): Array<PluginOption> {
176188
envName: VITE_ENVIRONMENTS.client,
177189
getRuntimeCode: () =>
178190
`import { createServerReference } from "${normalizePath(
179-
fileURLToPath(new URL("../server/server-runtime", import.meta.url))
191+
fileURLToPath(new URL("../server/server-runtime", import.meta.url)),
180192
)}"`,
181193
replacer: opts => `createServerReference('${opts.functionId}')`,
182194
},
@@ -185,7 +197,7 @@ export function solidStart(options?: SolidStartOptions): Array<PluginOption> {
185197
envName: VITE_ENVIRONMENTS.server,
186198
getRuntimeCode: () =>
187199
`import { createServerReference } from '${normalizePath(
188-
fileURLToPath(new URL("../server/server-fns-runtime", import.meta.url))
200+
fileURLToPath(new URL("../server/server-fns-runtime", import.meta.url)),
189201
)}'`,
190202
replacer: opts => `createServerReference(${opts.fn}, '${opts.functionId}')`,
191203
},
@@ -194,7 +206,7 @@ export function solidStart(options?: SolidStartOptions): Array<PluginOption> {
194206
envName: VITE_ENVIRONMENTS.server,
195207
getRuntimeCode: () =>
196208
`import { createServerReference } from '${normalizePath(
197-
fileURLToPath(new URL("../server/server-fns-runtime", import.meta.url))
209+
fileURLToPath(new URL("../server/server-fns-runtime", import.meta.url)),
198210
)}'`,
199211
replacer: opts => `createServerReference(${opts.fn}, '${opts.functionId}')`,
200212
},

0 commit comments

Comments
 (0)