Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions apps/tests/src/routes/server-function-file.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { createEffect, createSignal } from "solid-js";

async function ping(file: File) {
"use server";
return await file.text();
}

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

createEffect(async () => {
const file = new File(['Hello, World!'], 'hello-world.txt');
const result = await ping(file);
const value = await file.text();
setOutput(prev => ({ ...prev, result: value === result }));
});

return (
<main>
<span id="server-fn-test">{JSON.stringify(output())}</span>
</main>
);
}
35 changes: 35 additions & 0 deletions apps/tests/src/routes/server-function-iterator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { createEffect, createSignal } from "solid-js";

async function ping(value: Date) {
"use server";

const current = [
value,
{
name: 'example',
*[Symbol.iterator]() {
yield 'foo';
yield 'bar';
yield 'baz';
}
}
];

return current;
}

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

createEffect(async () => {
const value = new Date();
const result = await ping(value);
setOutput((prev) => ({ ...prev, result: value.toString() === result[0].toString() }));
});

return (
<main>
<span id="server-fn-test">{JSON.stringify(output())}</span>
</main>
);
}
28 changes: 24 additions & 4 deletions apps/tests/src/routes/server-function-ping.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,38 @@
import { createEffect, createSignal } from "solid-js";

async function ping(value: string) {
async function sleep(value: unknown, ms: number) {
return new Promise((res) => {
setTimeout(res, ms, value);
})
}

async function ping(value: Date) {
"use server";

return await Promise.resolve(value);
const current = [
value,
{
name: 'example',
async *[Symbol.asyncIterator]() {
yield sleep('foo', 5000);
yield sleep('bar', 5000);
yield sleep('baz', 5000);
}
}
];

return current;
}

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

createEffect(async () => {
const value = `${Math.random() * 1000}`;
const value = new Date();
const result = await ping(value);
setOutput(prev => ({ ...prev, result: value === result }));
await ping(value);
console.log(result);
setOutput((prev) => ({ ...prev, result: value.toString() === result[0].toString() }));
});

return (
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't have to stay but you want to watch the inspector track streams, then enjoy

Expand Down
38 changes: 38 additions & 0 deletions apps/tests/src/routes/server-function-plugin.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { createEffect, createSignal } from "solid-js";

async function sleep(value: unknown, ms: number) {
return new Promise((res) => {
setTimeout(res, ms, value);
})
}

async function ping(value: URLSearchParams, clone: URLSearchParams) {
"use server";

const current = [
value.toString() === clone.toString(),
value,
clone,
] as const;

return current;
}

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

createEffect(async () => {
const value = new URLSearchParams([
['foo', 'bar'],
['hello', 'world'],
]);
const result = await ping(value, value);
setOutput((prev) => ({ ...prev, result: result[0] }));
});

return (
<main>
<span id="server-fn-test">{JSON.stringify(output())}</span>
</main>
);
}
39 changes: 39 additions & 0 deletions apps/tests/src/routes/server-function-readable-stream.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { createEffect, createSignal } from "solid-js";

async function sleep<T>(value: T, ms: number) {
return new Promise<T>((res) => {
setTimeout(res, ms, value);
})
}

async function ping(value: ReadableStream<string>) {
"use server";
return value;
}

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

createEffect(async () => {
const result = await ping(
new ReadableStream({
async start(controller) {
controller.enqueue(await sleep('foo', 100));
controller.enqueue(await sleep('bar', 100));
controller.enqueue(await sleep('baz', 100));
controller.close();
},
})
);

const reader = result.getReader();
const first = await reader.read();
setOutput((prev) => ({ ...prev, result: first.value === 'foo' }));
});

return (
<main>
<span id="server-fn-test">{JSON.stringify(output())}</span>
</main>
);
}
4 changes: 0 additions & 4 deletions apps/tests/test-results/.last-run.json

This file was deleted.

2 changes: 1 addition & 1 deletion packages/start/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
"solid-js": "^1.9.9",
"source-map-js": "^1.2.1",
"srvx": "^0.9.1",
"terracotta": "^1.0.6",
"terracotta": "^1.1.0",
"vite-plugin-solid": "^2.11.9"
},
"engines": {
Expand Down
2 changes: 1 addition & 1 deletion packages/start/src/server/serialization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export function serializeToJSONStream(value: any) {
});
}

class SerovalChunkReader {
export class SerovalChunkReader {
reader: ReadableStreamDefaultReader<Uint8Array>;
buffer: Uint8Array;
done: boolean;
Expand Down
34 changes: 25 additions & 9 deletions packages/start/src/server/server-functions-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ export async function handleServerFunction(h3Event: H3Event) {
h3Event.res.headers.set("content-type", "text/javascript");
return serializeToJSStream(instance, result);
}
h3Event.res.headers.set("content-type", "text/plain");
return serializeToJSONStream(result);
} catch (x) {
if (x instanceof Response) {
Expand All @@ -119,14 +118,18 @@ export async function handleServerFunction(h3Event: H3Event) {
// forward headers
if ((x as any).headers) mergeResponseHeaders(h3Event, (x as any).headers);
// forward non-redirect statuses
if ((x as any).status && (!instance || (x as any).status < 300 || (x as any).status >= 400))
if (
(x as any).status &&
(!instance || (x as any).status < 300 || (x as any).status >= 400)
)
h3Event.res.status = (x as any).status;
if ((x as any).customBody) {
x = await (x as any).customBody();
} else if ((x as any).body == null) x = null;
h3Event.res.headers.set("X-Error", "true");
} else if (instance) {
const error = x instanceof Error ? x.message : typeof x === "string" ? x : "true";
const error =
x instanceof Error ? x.message : typeof x === "string" ? x : "true";

h3Event.res.headers.set("X-Error", error.replace(/[\r\n]+/g, ""));
} else {
Expand All @@ -149,14 +152,18 @@ export async function handleServerFunction(h3Event: H3Event) {
h3Event.res.headers.set("content-type", "text/javascript");
return serializeToJSStream(instance, x);
}
h3Event.res.headers.set("content-type", "text/plain");
return serializeToJSONStream(x);
}
return x;
}
}

function handleNoJS(result: any, request: Request, parsed: any[], thrown?: boolean) {
function handleNoJS(
result: any,
request: Request,
parsed: any[],
thrown?: boolean,
) {
const url = new URL(request.url);
const isError = result instanceof Error;
let statusCode = 302;
Expand All @@ -166,7 +173,10 @@ function handleNoJS(result: any, request: Request, parsed: any[], thrown?: boole
if (result.headers.has("Location")) {
headers.set(
`Location`,
new URL(result.headers.get("Location")!, url.origin + import.meta.env.BASE_URL).toString(),
new URL(
result.headers.get("Location")!,
url.origin + import.meta.env.BASE_URL,
).toString(),
);
statusCode = getExpectedRedirectStatus(result);
}
Expand All @@ -183,7 +193,10 @@ function handleNoJS(result: any, request: Request, parsed: any[], thrown?: boole
result: isError ? result.message : result,
thrown: thrown,
error: isError,
input: [...parsed.slice(0, -1), [...parsed[parsed.length - 1].entries()]],
input: [
...parsed.slice(0, -1),
[...parsed[parsed.length - 1].entries()],
],
}),
)}; Secure; HttpOnly;`,
);
Expand All @@ -209,7 +222,7 @@ function createSingleFlightHeaders(sourceEvent: FetchEvent) {
// useH3Internals = true;
// sourceEvent.nativeEvent.node.req.headers.cookie = "";
// }
SetCookies.forEach(cookie => {
SetCookies.forEach((cookie) => {
if (!cookie) return;
const { maxAge, expires, name, value } = parseSetCookie(cookie);
if (maxAge != null && maxAge <= 0) {
Expand All @@ -230,7 +243,10 @@ function createSingleFlightHeaders(sourceEvent: FetchEvent) {

return headers;
}
async function handleSingleFlight(sourceEvent: FetchEvent, result: any): Promise<Response> {
async function handleSingleFlight(
sourceEvent: FetchEvent,
result: any,
): Promise<Response> {
let revalidate: string[];
let url = new URL(sourceEvent.request.headers.get("referer")!).toString();
if (result instanceof Response) {
Expand Down
27 changes: 23 additions & 4 deletions packages/start/src/server/server-runtime.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { type Component } from "solid-js";
import {
pushRequest,
pushResponse,
} from "../shared/server-function-inspector/server-function-tracker";
import {
// serializeToJSONStream,
serializeToJSONString,
Expand All @@ -12,8 +16,13 @@ import {

let INSTANCE = 0;

function createRequest(base: string, id: string, instance: string, options: RequestInit) {
return fetch(base, {
async function createRequest(
base: string,
id: string,
instance: string,
options: RequestInit,
) {
const request = new Request(base, {
method: "POST",
...options,
headers: {
Expand All @@ -22,6 +31,14 @@ function createRequest(base: string, id: string, instance: string, options: Requ
"X-Server-Instance": instance,
},
});
if (import.meta.env.DEV) {
pushRequest(id, instance, request.clone());
}
const response = await fetch(request);
if (import.meta.env.DEV) {
pushResponse(id, instance, response.clone());
}
return response;
}

async function initializeResponse(
Expand Down Expand Up @@ -100,7 +117,8 @@ export function createServerReference(id: string) {
let baseURL = import.meta.env.BASE_URL ?? "/";
if (!baseURL.endsWith("/")) baseURL += "/";

const fn = (...args: any[]) => fetchServerFunction(`${baseURL}_server`, id, {}, args);
const fn = (...args: any[]) =>
fetchServerFunction(`${baseURL}_server`, id, {}, args);

return new Proxy(fn, {
get(target, prop, receiver) {
Expand All @@ -114,7 +132,8 @@ export function createServerReference(id: string) {
const url = `${baseURL}_server?id=${encodeURIComponent(id)}`;
return (options: RequestInit) => {
const fn = async (...args: any[]) => {
const encodeArgs = options.method && options.method.toUpperCase() === "GET";
const encodeArgs =
options.method && options.method.toUpperCase() === "GET";
return fetchServerFunction(
encodeArgs
? url +
Expand Down
Loading
Loading