diff --git a/packages/start/config/server-fns-runtime.jsx b/packages/start/config/server-fns-runtime.jsx index 95c833743..d142f30a2 100644 --- a/packages/start/config/server-fns-runtime.jsx +++ b/packages/start/config/server-fns-runtime.jsx @@ -9,6 +9,7 @@ export function createServerReference(fn, id, name) { if (prop === "url") { return `${baseURL}/_server?id=${encodeURIComponent(id)}&name=${encodeURIComponent(name)}`; } + if (prop === "GET") return receiver; }, apply(target, thisArg, args) { const ogEvt = getRequestEvent(); diff --git a/packages/start/config/server-handler.js b/packages/start/config/server-handler.js index 2e6f0c888..a2376209d 100644 --- a/packages/start/config/server-handler.js +++ b/packages/start/config/server-handler.js @@ -16,16 +16,13 @@ import { sharedConfig } from "solid-js"; /* @ts-ignore */ import { provideRequestEvent } from "solid-js/web/storage"; import invariant from "vinxi/lib/invariant"; -import { - eventHandler, - setHeader -} from "vinxi/server"; +import { eventHandler, setHeader } from "vinxi/server"; import { getFetchEvent } from "../server/middleware"; function createChunk(data) { const bytes = data.length; const baseHex = bytes.toString(16); - const totalHex = '00000000'.substring(0, 8 - baseHex.length) + baseHex; // 32-bit + const totalHex = "00000000".substring(0, 8 - baseHex.length) + baseHex; // 32-bit return new TextEncoder().encode(`;0x${totalHex};${data}`); } @@ -47,7 +44,9 @@ function serializeToStream(id, value) { URLPlugin ], onSerialize(data, initial) { - controller.enqueue(createChunk(initial ? `(${getCrossReferenceHeader(id)},${data})` : data)); + controller.enqueue( + createChunk(initial ? `(${getCrossReferenceHeader(id)},${data})` : data) + ); }, onDone() { controller.close(); @@ -61,7 +60,6 @@ function serializeToStream(id, value) { } async function handleServerFunction(h3Event) { - invariant(h3Event.method === "POST", `Invalid method ${h3Event.method}. Expected POST.`); const event = getFetchEvent(h3Event); const request = event.request; @@ -84,31 +82,33 @@ async function handleServerFunction(h3Event) { let parsed = []; // grab bound arguments from url when no JS - if (!instance) { + if (!instance || h3Event.method === "GET") { const args = url.searchParams.get("args"); if (args) JSON.parse(args).forEach(arg => parsed.push(arg)); } - const contentType = request.headers.get("content-type"); - if ( - contentType.startsWith("multipart/form-data") || - contentType.startsWith("application/x-www-form-urlencoded") - ) { - parsed.push(await request.formData()); - } else { - parsed = fromJSON(await request.json(), { - plugins: [ - CustomEventPlugin, - DOMExceptionPlugin, - EventPlugin, - FormDataPlugin, - HeadersPlugin, - ReadableStreamPlugin, - RequestPlugin, - ResponsePlugin, - URLSearchParamsPlugin, - URLPlugin - ] - }); + if (h3Event.method === "POST") { + const contentType = request.headers.get("content-type"); + if ( + contentType.startsWith("multipart/form-data") || + contentType.startsWith("application/x-www-form-urlencoded") + ) { + parsed.push(await request.formData()); + } else { + parsed = fromJSON(await request.json(), { + plugins: [ + CustomEventPlugin, + DOMExceptionPlugin, + EventPlugin, + FormDataPlugin, + HeadersPlugin, + ReadableStreamPlugin, + RequestPlugin, + ResponsePlugin, + URLSearchParamsPlugin, + URLPlugin + ] + }); + } } try { const result = await provideRequestEvent(event, () => { diff --git a/packages/start/config/server-runtime.jsx b/packages/start/config/server-runtime.jsx index 39637c3b8..f88306a3a 100644 --- a/packages/start/config/server-runtime.jsx +++ b/packages/start/config/server-runtime.jsx @@ -9,14 +9,14 @@ import { RequestPlugin, ResponsePlugin, URLPlugin, - URLSearchParamsPlugin, -} from 'seroval-plugins/web'; + URLSearchParamsPlugin +} from "seroval-plugins/web"; import { createIslandReference } from "../server/islands"; class SerovalChunkReader { constructor(stream) { this.reader = stream.getReader(); - this.buffer = ''; + this.buffer = ""; this.done = false; } @@ -33,12 +33,12 @@ class SerovalChunkReader { async next() { // Check if the buffer is empty - if (this.buffer === '') { + if (this.buffer === "") { // if we are already done... if (this.done) { return { done: true, - value: undefined, + value: undefined }; } // Otherwise, read a new chunk @@ -55,7 +55,7 @@ class SerovalChunkReader { // If it's not enough, and the reader is done // then the chunk is invalid. if (this.done) { - throw new Error('Malformed server function stream.') + throw new Error("Malformed server function stream."); } // Otherwise, we read more chunks await this.readChunk(); @@ -67,7 +67,7 @@ class SerovalChunkReader { // Deserialize the chunk return { done: false, - value: deserialize(partial), + value: deserialize(partial) }; } @@ -117,31 +117,48 @@ function createRequest(base, id, instance, body, contentType) { }); } -async function fetchServerFunction(base, id, args) { +async function fetchServerFunction(base, id, method, args) { const instance = `server-fn:${INSTANCE++}`; - const response = await (args.length === 1 && args[0] instanceof FormData + const response = await (method === "GET" + ? fetch(base + `/?args=${JSON.stringify(args)}`, { + headers: { + "x-server-id": id, + "x-server-instance": instance + } + }) + : args.length === 1 && args[0] instanceof FormData ? createRequest(base, id, instance, args[0]) - : createRequest(base, id, instance, JSON.stringify(await Promise.resolve(toJSONAsync(args, { - plugins: [ - CustomEventPlugin, - DOMExceptionPlugin, - EventPlugin, - FormDataPlugin, - HeadersPlugin, - ReadableStreamPlugin, - RequestPlugin, - ResponsePlugin, - URLSearchParamsPlugin, - URLPlugin, - ], - }))), "application/json")); + : createRequest( + base, + id, + instance, + JSON.stringify( + await Promise.resolve( + toJSONAsync(args, { + plugins: [ + CustomEventPlugin, + DOMExceptionPlugin, + EventPlugin, + FormDataPlugin, + HeadersPlugin, + ReadableStreamPlugin, + RequestPlugin, + ResponsePlugin, + URLSearchParamsPlugin, + URLPlugin + ] + }) + ) + ), + "application/json" + )); if (response.headers.get("Location")) throw response; if (response.headers.get("X-Revalidate")) { /* ts-ignore-next-line */ response.customBody = () => { return deserializeStream(instance, response); - } + }; throw response; } const contentType = response.headers.get("Content-Type"); @@ -166,9 +183,12 @@ export function createServerReference(fn, id, name) { if (prop === "url") { return `${baseURL}/_server?id=${encodeURIComponent(id)}&name=${encodeURIComponent(name)}`; } + if (prop === "GET") { + return (...args) => fetchServerFunction(`${baseURL}/_server`, `${id}#${name}`, "GET", args); + } }, apply(target, thisArg, args) { - return fetchServerFunction(`${baseURL}/_server`, `${id}#${name}`, args); + return fetchServerFunction(`${baseURL}/_server`, `${id}#${name}`, "POST", args); } }); }