Skip to content

Commit 6109593

Browse files
committed
single flight mutations
1 parent b90d979 commit 6109593

2 files changed

Lines changed: 121 additions & 89 deletions

File tree

packages/start/src/server/index.tsx

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
import { eventHandler, getResponseHeaders, H3Event, sendStream, sendWebResponse } from "h3";
1+
import {
2+
eventHandler,
3+
getCookie,
4+
getResponseHeaders,
5+
H3Event,
6+
sendStream,
7+
sendWebResponse,
8+
setCookie
9+
} from "h3";
210
import { provideRequestEvent } from "solid-js/web/storage";
311
import type { JSX } from "solid-js";
412
import { renderToStream, renderToString, renderToStringAsync } from "solid-js/web";
@@ -30,7 +38,29 @@ const SERVER_FN_BASE = "/_server";
3038
// return 302;
3139
// }
3240

33-
async function createPageEvent(ctx: FetchEvent) {
41+
function initFromFlash(ctx: FetchEvent) {
42+
const flash = getCookie(ctx.nativeEvent, "flash");
43+
if (!flash) return;
44+
try {
45+
let param = JSON.parse(flash);
46+
if (!param || !param.result) return;
47+
const input = [...param.input.slice(0, -1), new Map(param.input[param.input.length - 1])];
48+
const result = param.error ? new Error(param.result) : param.result;
49+
return {
50+
input,
51+
url: param.url,
52+
pending: false,
53+
result: param.thrown ? undefined : result,
54+
error: param.thrown ? result : undefined
55+
};
56+
} catch (e) {
57+
console.error(e);
58+
} finally {
59+
setCookie(ctx.nativeEvent, "flash", "", { maxAge: 0 });
60+
}
61+
}
62+
63+
export async function createPageEvent(ctx: FetchEvent) {
3464
// const clientManifest = import.meta.env.MANIFEST["client"]!;
3565
// const serverManifest = import.meta.env.MANIFEST["server"]!;
3666
// ctx.response.headers.set("Content-Type", "text/html");
@@ -39,7 +69,7 @@ async function createPageEvent(ctx: FetchEvent) {
3969
const pageEvent: PageEvent = Object.assign(ctx, {
4070
manifest: manifest.routes,
4171
assets: [
42-
...(await getClientEntryCssTags())
72+
...getClientEntryCssTags()
4373
// not needed anymore?
4474
// ...(import.meta.env.DEV
4575
// ? await clientManifest.inputs[import.meta.env.START_APP]!.assets()
@@ -50,9 +80,9 @@ async function createPageEvent(ctx: FetchEvent) {
5080
// )
5181
// : [])
5282
],
53-
// router: {
54-
// // submission: initFromFlash(ctx) as any
55-
// },
83+
router: {
84+
submission: initFromFlash(ctx) as any
85+
},
5686
routes: createRoutes(),
5787
// // prevUrl: prevPath || "",
5888
// // mutation: mutation,

packages/start/src/server/server-functions-handler.ts

Lines changed: 85 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ import serverFnManifest from "solidstart:server-fn-manifest";
3030
import { isRunnableDevEnvironment } from "vite";
3131
import { getFetchEvent, mergeResponseHeaders } from "./fetchEvent.js";
3232
import { getExpectedRedirectStatus } from "./util.js";
33+
import { FetchEvent, PageEvent } from "./types.js";
34+
import { createPageEvent } from "./index.jsx";
3335

3436
function createChunk(data: string) {
3537
const encodeData = new TextEncoder().encode(data);
@@ -218,9 +220,9 @@ export async function handleServerFunction(h3Event: H3Event) {
218220
return serverFunction(...parsed);
219221
});
220222

221-
// if (singleFlight && instance) {
222-
// result = await handleSingleFlight(event, result);
223-
// }
223+
if (singleFlight && instance) {
224+
result = await handleSingleFlight(event, result);
225+
}
224226

225227
// handle responses
226228
if (result instanceof Response) {
@@ -245,9 +247,9 @@ export async function handleServerFunction(h3Event: H3Event) {
245247
return serializeToStream(instance, result);
246248
} catch (x) {
247249
if (x instanceof Response) {
248-
// if (singleFlight && instance) {
249-
// x = await handleSingleFlight(event, x);
250-
// }
250+
if (singleFlight && instance) {
251+
x = await handleSingleFlight(event, x);
252+
}
251253
// forward headers
252254
if ((x as any).headers) mergeResponseHeaders(h3Event, (x as any).headers);
253255
// forward non-redirect statuses
@@ -309,83 +311,83 @@ function handleNoJS(result: any, request: Request, parsed: any[], thrown?: boole
309311
});
310312
}
311313

312-
// let App: any;
313-
// function createSingleFlightHeaders(sourceEvent: FetchEvent) {
314-
// // cookie handling logic is pretty simplistic so this might be imperfect
315-
// // unclear if h3 internals are available on all platforms but we need a way to
316-
// // update request headers on the underlying H3 event.
314+
let App: any;
315+
function createSingleFlightHeaders(sourceEvent: FetchEvent) {
316+
// cookie handling logic is pretty simplistic so this might be imperfect
317+
// unclear if h3 internals are available on all platforms but we need a way to
318+
// update request headers on the underlying H3 event.
317319

318-
// const headers = new Headers(sourceEvent.request.headers);
319-
// const cookies = parseCookies(sourceEvent.nativeEvent);
320-
// const SetCookies = sourceEvent.response.headers.getSetCookie();
321-
// headers.delete("cookie");
322-
// let useH3Internals = false;
323-
// if (sourceEvent.nativeEvent.node?.req) {
324-
// useH3Internals = true;
325-
// sourceEvent.nativeEvent.node.req.headers.cookie = "";
326-
// }
327-
// SetCookies.forEach(cookie => {
328-
// if (!cookie) return;
329-
// const keyValue = cookie.split(";")[0]!;
330-
// const [key, value] = keyValue.split("=");
331-
// key && value && (cookies[key] = value);
332-
// });
333-
// Object.entries(cookies).forEach(([key, value]) => {
334-
// headers.append("cookie", `${key}=${value}`);
335-
// useH3Internals && (sourceEvent.nativeEvent.node.req.headers.cookie += `${key}=${value};`);
336-
// });
320+
const headers = new Headers(sourceEvent.request.headers);
321+
const cookies = parseCookies(sourceEvent.nativeEvent);
322+
const SetCookies = sourceEvent.response.headers.getSetCookie();
323+
headers.delete("cookie");
324+
let useH3Internals = false;
325+
if (sourceEvent.nativeEvent.node?.req) {
326+
useH3Internals = true;
327+
sourceEvent.nativeEvent.node.req.headers.cookie = "";
328+
}
329+
SetCookies.forEach(cookie => {
330+
if (!cookie) return;
331+
const keyValue = cookie.split(";")[0]!;
332+
const [key, value] = keyValue.split("=");
333+
key && value && (cookies[key] = value);
334+
});
335+
Object.entries(cookies).forEach(([key, value]) => {
336+
headers.append("cookie", `${key}=${value}`);
337+
useH3Internals && (sourceEvent.nativeEvent.node.req.headers.cookie += `${key}=${value};`);
338+
});
337339

338-
// return headers;
339-
// }
340-
// async function handleSingleFlight(sourceEvent: FetchEvent, result: any): Promise<Response> {
341-
// let revalidate: string[];
342-
// let url = new URL(sourceEvent.request.headers.get("referer")!).toString();
343-
// if (result instanceof Response) {
344-
// if (result.headers.has("X-Revalidate"))
345-
// revalidate = result.headers.get("X-Revalidate")!.split(",");
346-
// if (result.headers.has("Location"))
347-
// url = new URL(
348-
// result.headers.get("Location")!,
349-
// new URL(sourceEvent.request.url).origin + import.meta.env.SERVER_BASE_URL
350-
// ).toString();
351-
// }
352-
// const event = { ...sourceEvent } as PageEvent;
353-
// event.request = new Request(url, { headers: createSingleFlightHeaders(sourceEvent) });
354-
// return await provideRequestEvent(event, async () => {
355-
// await createPageEvent(event);
356-
// /* @ts-ignore */
357-
// App || (App = (await import("#start/app")).default);
358-
// /* @ts-ignore */
359-
// event.router.dataOnly = revalidate || true;
360-
// /* @ts-ignore */
361-
// event.router.previousUrl = sourceEvent.request.headers.get("referer");
362-
// try {
363-
// renderToString(() => {
364-
// /* @ts-ignore */
365-
// sharedConfig.context.event = event;
366-
// App();
367-
// });
368-
// } catch (e) {
369-
// console.log(e);
370-
// }
340+
return headers;
341+
}
342+
async function handleSingleFlight(sourceEvent: FetchEvent, result: any): Promise<Response> {
343+
let revalidate: string[];
344+
let url = new URL(sourceEvent.request.headers.get("referer")!).toString();
345+
if (result instanceof Response) {
346+
if (result.headers.has("X-Revalidate"))
347+
revalidate = result.headers.get("X-Revalidate")!.split(",");
348+
if (result.headers.has("Location"))
349+
url = new URL(
350+
result.headers.get("Location")!,
351+
new URL(sourceEvent.request.url).origin + import.meta.env.SERVER_BASE_URL
352+
).toString();
353+
}
354+
const event = { ...sourceEvent } as PageEvent;
355+
event.request = new Request(url, { headers: createSingleFlightHeaders(sourceEvent) });
356+
return await provideRequestEvent(event, async () => {
357+
await createPageEvent(event);
358+
/* @ts-ignore */
359+
App || (App = (await import("#start/app")).default);
360+
/* @ts-ignore */
361+
event.router.dataOnly = revalidate || true;
362+
/* @ts-ignore */
363+
event.router.previousUrl = sourceEvent.request.headers.get("referer");
364+
try {
365+
renderToString(() => {
366+
/* @ts-ignore */
367+
sharedConfig.context.event = event;
368+
App();
369+
});
370+
} catch (e) {
371+
console.log(e);
372+
}
371373

372-
// /* @ts-ignore */
373-
// const body = event.router.data;
374-
// if (!body) return result;
375-
// let containsKey = false;
376-
// for (const key in body) {
377-
// if (body[key] === undefined) delete body[key];
378-
// else containsKey = true;
379-
// }
380-
// if (!containsKey) return result;
381-
// if (!(result instanceof Response)) {
382-
// body["_$value"] = result;
383-
// result = new Response(null, { status: 200 });
384-
// } else if ((result as any).customBody) {
385-
// body["_$value"] = (result as any).customBody();
386-
// }
387-
// result.customBody = () => body;
388-
// result.headers.set("X-Single-Flight", "true");
389-
// return result;
390-
// });
391-
// }
374+
/* @ts-ignore */
375+
const body = event.router.data;
376+
if (!body) return result;
377+
let containsKey = false;
378+
for (const key in body) {
379+
if (body[key] === undefined) delete body[key];
380+
else containsKey = true;
381+
}
382+
if (!containsKey) return result;
383+
if (!(result instanceof Response)) {
384+
body["_$value"] = result;
385+
result = new Response(null, { status: 200 });
386+
} else if ((result as any).customBody) {
387+
body["_$value"] = (result as any).customBody();
388+
}
389+
result.customBody = () => body;
390+
result.headers.set("X-Single-Flight", "true");
391+
return result;
392+
});
393+
}

0 commit comments

Comments
 (0)