Skip to content

Commit eeff49a

Browse files
lxsmnsycbirkskyum
andauthored
feat: server function inspector (#2049)
Co-authored-by: Birk Skyum <[email protected]>
1 parent 38faf5d commit eeff49a

62 files changed

Lines changed: 3385 additions & 475 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.changeset/chilly-rabbits-lie.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+
add new dev toolbar
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(file: File) {
4+
"use server";
5+
return await file.text();
6+
}
7+
8+
export default function App() {
9+
const [output, setOutput] = createSignal<{ result?: boolean }>({});
10+
11+
createEffect(async () => {
12+
const file = new File(['Hello, World!'], 'hello-world.txt');
13+
const result = await ping(file);
14+
const value = await file.text();
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+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { createEffect, createSignal } from "solid-js";
2+
3+
async function ping(value: Date) {
4+
"use server";
5+
6+
const current = [
7+
value,
8+
{
9+
name: 'example',
10+
*[Symbol.iterator]() {
11+
yield 'foo';
12+
yield 'bar';
13+
yield 'baz';
14+
}
15+
}
16+
];
17+
18+
return current;
19+
}
20+
21+
export default function App() {
22+
const [output, setOutput] = createSignal<{ result?: boolean }>({});
23+
24+
createEffect(async () => {
25+
const value = new Date();
26+
const result = await ping(value);
27+
setOutput((prev) => ({ ...prev, result: value.toString() === result[0].toString() }));
28+
});
29+
30+
return (
31+
<main>
32+
<span id="server-fn-test">{JSON.stringify(output())}</span>
33+
</main>
34+
);
35+
}

‎apps/tests/src/routes/server-function-ping.tsx‎

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,38 @@
11
import { createEffect, createSignal } from "solid-js";
22

3-
async function ping(value: string) {
3+
async function sleep(value: unknown, ms: number) {
4+
return new Promise((res) => {
5+
setTimeout(res, ms, value);
6+
})
7+
}
8+
9+
async function ping(value: Date) {
410
"use server";
511

6-
return await Promise.resolve(value);
12+
const current = [
13+
value,
14+
{
15+
name: 'example',
16+
async *[Symbol.asyncIterator]() {
17+
yield sleep('foo', 5000);
18+
yield sleep('bar', 5000);
19+
yield sleep('baz', 5000);
20+
}
21+
}
22+
];
23+
24+
return current;
725
}
826

927
export default function App() {
1028
const [output, setOutput] = createSignal<{ result?: boolean }>({});
1129

1230
createEffect(async () => {
13-
const value = `${Math.random() * 1000}`;
31+
const value = new Date();
1432
const result = await ping(value);
15-
setOutput(prev => ({ ...prev, result: value === result }));
33+
await ping(value);
34+
console.log(result);
35+
setOutput((prev) => ({ ...prev, result: value.toString() === result[0].toString() }));
1636
});
1737

1838
return (
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { createEffect, createSignal } from "solid-js";
2+
3+
async function sleep(value: unknown, ms: number) {
4+
return new Promise((res) => {
5+
setTimeout(res, ms, value);
6+
})
7+
}
8+
9+
async function ping(value: URLSearchParams, clone: URLSearchParams) {
10+
"use server";
11+
12+
const current = [
13+
value.toString() === clone.toString(),
14+
value,
15+
clone,
16+
] as const;
17+
18+
return current;
19+
}
20+
21+
export default function App() {
22+
const [output, setOutput] = createSignal<{ result?: boolean }>({});
23+
24+
createEffect(async () => {
25+
const value = new URLSearchParams([
26+
['foo', 'bar'],
27+
['hello', 'world'],
28+
]);
29+
const result = await ping(value, value);
30+
setOutput((prev) => ({ ...prev, result: result[0] }));
31+
});
32+
33+
return (
34+
<main>
35+
<span id="server-fn-test">{JSON.stringify(output())}</span>
36+
</main>
37+
);
38+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { createEffect, createSignal } from "solid-js";
2+
3+
async function sleep<T>(value: T, ms: number) {
4+
return new Promise<T>((res) => {
5+
setTimeout(res, ms, value);
6+
})
7+
}
8+
9+
async function ping(value: ReadableStream<string>) {
10+
"use server";
11+
return value;
12+
}
13+
14+
export default function App() {
15+
const [output, setOutput] = createSignal<{ result?: boolean }>({});
16+
17+
createEffect(async () => {
18+
const result = await ping(
19+
new ReadableStream({
20+
async start(controller) {
21+
controller.enqueue(await sleep('foo', 100));
22+
controller.enqueue(await sleep('bar', 100));
23+
controller.enqueue(await sleep('baz', 100));
24+
controller.close();
25+
},
26+
})
27+
);
28+
29+
const reader = result.getReader();
30+
const first = await reader.read();
31+
setOutput((prev) => ({ ...prev, result: first.value === 'foo' }));
32+
});
33+
34+
return (
35+
<main>
36+
<span id="server-fn-test">{JSON.stringify(output())}</span>
37+
</main>
38+
);
39+
}

‎apps/tests/src/routes/treeshaking/(no-side-effects).tsx‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { createAsync } from "@solidjs/router";
2+
import { createEffect } from "solid-js";
23

34
const a = "myTreeshakingTestUniqueString1";
45

‎packages/start/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@
7676
"solid-js": "^1.9.11",
7777
"source-map-js": "^1.2.1",
7878
"srvx": "^0.12.0",
79-
"terracotta": "^1.1.0",
79+
"terracotta": "^1.1.1",
8080
"vite-plugin-solid": "^2.11.11"
8181
},
8282
"engines": {

‎packages/start/src/fns/client.ts‎

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,15 @@
11
import { type Component } from "solid-js";
2+
import { pushRequest, pushResponse } from "../shared/dev-toolbar/functions/tracker.ts";
23
import {
34
// serializeToJSONStream,
45
serializeToJSONString,
56
} from "./serialization.ts";
6-
import {
7-
BODY_FORMAT_KEY,
8-
BodyFormat,
9-
extractBody,
10-
getHeadersAndBody,
11-
} from "./shared.ts";
7+
import { BODY_FORMAT_KEY, BodyFormat, extractBody, getHeadersAndBody } from "./shared.ts";
128

139
let INSTANCE = 0;
1410

15-
function createRequest(base: string, id: string, instance: string, options: RequestInit) {
16-
return fetch(base, {
11+
async function createRequest(base: string, id: string, instance: string, options: RequestInit) {
12+
const request = new Request(base, {
1713
method: "POST",
1814
...options,
1915
headers: {
@@ -22,6 +18,15 @@ function createRequest(base: string, id: string, instance: string, options: Requ
2218
"X-Server-Instance": instance,
2319
},
2420
});
21+
if (import.meta.env.DEV) {
22+
console.log(pushRequest);
23+
pushRequest(id, instance, request.clone());
24+
}
25+
const response = await fetch(request);
26+
if (import.meta.env.DEV) {
27+
pushResponse(id, instance, response.clone());
28+
}
29+
return response;
2530
}
2631

2732
async function initializeResponse(

‎packages/start/src/fns/handler.ts‎

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,6 @@ export async function handleServerFunction(h3Event: H3Event) {
124124
h3Event.res.headers.set("content-type", "text/javascript");
125125
return serializeToJSStream(instance, result);
126126
}
127-
h3Event.res.headers.set("content-type", "text/plain");
128127
return serializeToJSONStream(result);
129128
} catch (x) {
130129
if (x instanceof Response) {
@@ -134,14 +133,18 @@ export async function handleServerFunction(h3Event: H3Event) {
134133
// forward headers
135134
if ((x as any).headers) mergeResponseHeaders(h3Event, (x as any).headers);
136135
// forward non-redirect statuses
137-
if ((x as any).status && (!instance || (x as any).status < 300 || (x as any).status >= 400))
136+
if (
137+
(x as any).status &&
138+
(!instance || (x as any).status < 300 || (x as any).status >= 400)
139+
)
138140
h3Event.res.status = (x as any).status;
139141
if ((x as any).customBody) {
140142
x = await (x as any).customBody();
141143
} else if ((x as any).body == null) x = null;
142144
h3Event.res.headers.set("X-Error", "true");
143145
} else if (instance) {
144-
const error = x instanceof Error ? x.message : typeof x === "string" ? x : "true";
146+
const error =
147+
x instanceof Error ? x.message : typeof x === "string" ? x : "true";
145148

146149
h3Event.res.headers.set("X-Error", toHeaderValue(error));
147150
} else {
@@ -164,7 +167,6 @@ export async function handleServerFunction(h3Event: H3Event) {
164167
h3Event.res.headers.set("content-type", "text/javascript");
165168
return serializeToJSStream(instance, x);
166169
}
167-
h3Event.res.headers.set("content-type", "text/plain");
168170
return serializeToJSONStream(x);
169171
}
170172
return x;
@@ -192,7 +194,10 @@ function handleNoJS(result: any, request: Request, parsed: any[], thrown?: boole
192194
if (result.headers.has("Location")) {
193195
headers.set(
194196
`Location`,
195-
new URL(result.headers.get("Location")!, url.origin + import.meta.env.BASE_URL).toString(),
197+
new URL(
198+
result.headers.get("Location")!,
199+
url.origin + import.meta.env.BASE_URL,
200+
).toString(),
196201
);
197202
statusCode = getExpectedRedirectStatus(result);
198203
}
@@ -209,7 +214,10 @@ function handleNoJS(result: any, request: Request, parsed: any[], thrown?: boole
209214
result: isError ? result.message : result,
210215
thrown: thrown,
211216
error: isError,
212-
input: [...parsed.slice(0, -1), [...parsed[parsed.length - 1].entries()]],
217+
input: [
218+
...parsed.slice(0, -1),
219+
[...parsed[parsed.length - 1].entries()],
220+
],
213221
}),
214222
)}; Secure; HttpOnly;`,
215223
);
@@ -235,7 +243,7 @@ export function createSingleFlightHeaders(sourceEvent: FetchEvent) {
235243
// useH3Internals = true;
236244
// sourceEvent.nativeEvent.node.req.headers.cookie = "";
237245
// }
238-
SetCookies.forEach(cookie => {
246+
SetCookies.forEach((cookie) => {
239247
if (!cookie) return;
240248
const { maxAge, expires, name, value } = parseSetCookie(cookie);
241249
if (maxAge != null && maxAge <= 0) {
@@ -256,7 +264,10 @@ export function createSingleFlightHeaders(sourceEvent: FetchEvent) {
256264

257265
return headers;
258266
}
259-
async function handleSingleFlight(sourceEvent: FetchEvent, result: any): Promise<Response> {
267+
async function handleSingleFlight(
268+
sourceEvent: FetchEvent,
269+
result: any,
270+
): Promise<Response> {
260271
let revalidate: string[];
261272
let url = new URL(sourceEvent.request.headers.get("referer")!).toString();
262273
if (result instanceof Response) {

0 commit comments

Comments
 (0)