From aa5e4bebea830466f7afb4263a04645ac04b803d Mon Sep 17 00:00:00 2001 From: Brenley Dueck Date: Sat, 31 Oct 2026 14:31:42 -0500 Subject: [PATCH] fix(server): collect every query for revalidate "*" in single flight --- .changeset/flight-revalidate-all.md | 5 +++++ src/data/query.ts | 2 +- src/server.ts | 10 ++++++--- test/server/flight-revalidate-all.spec.tsx | 26 ++++++++++++++++++++++ 4 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 .changeset/flight-revalidate-all.md create mode 100644 test/server/flight-revalidate-all.spec.tsx diff --git a/.changeset/flight-revalidate-all.md b/.changeset/flight-revalidate-all.md new file mode 100644 index 00000000..fe2e1663 --- /dev/null +++ b/.changeset/flight-revalidate-all.md @@ -0,0 +1,5 @@ +--- +"@solidjs/router": patch +--- + +A single-flight mutation answered with `revalidate: "*"` now collects every query on the target page instead of none. diff --git a/src/data/query.ts b/src/data/query.ts index 710be54b..98e6fedd 100644 --- a/src/data/query.ts +++ b/src/data/query.ts @@ -551,7 +551,7 @@ export function setupFlightDataConsumer(router: RouterContext) { }); } -const REVALIDATE_ALL = "*"; +export const REVALIDATE_ALL = "*"; /** * Reads an `X-Revalidate` header value into the keys the cache ops take. diff --git a/src/server.ts b/src/server.ts index 8384eedf..6529641f 100644 --- a/src/server.ts +++ b/src/server.ts @@ -34,6 +34,7 @@ import { peekLazySubtrees, resolveLazySubtree } from "./routing.js"; +import { REVALIDATE_ALL } from "./data/query.js"; import { extractSearchParams } from "./utils.js"; import { serverRouteArgs, serverRouteOf } from "./serverRouteShared.js"; import type { Branch, RouteDefinition, RoutePreloadFunc } from "./types.js"; @@ -94,7 +95,11 @@ function isRouterInstance( export function createFlightDataCollector( options: FlightDataCollectorOptions | RouterInstanceLike ): CollectFlightDataHook { - const { routes, rootPreload, base = "" } = isRouterInstance(options) + const { + routes, + rootPreload, + base = "" + } = isRouterInstance(options) ? { routes: options.routes, rootPreload: options.config.preload, base: options.config.base } : options; if (!routes) throw new Error("createFlightDataCollector requires `routes`"); @@ -127,7 +132,7 @@ export function createFlightDataCollector( const event = { ...sourceEvent } as RequestEvent; event.request = new Request(targetUrl, { headers: outcome.foldedHeaders }); event.router = { - dataOnly: revalidateKeys || true, + dataOnly: (!revalidateKeys?.includes(REVALIDATE_ALL) && revalidateKeys) || true, previousUrl, data: {} }; @@ -218,4 +223,3 @@ function runPreloads( }); } } - diff --git a/test/server/flight-revalidate-all.spec.tsx b/test/server/flight-revalidate-all.spec.tsx new file mode 100644 index 00000000..c16eefa0 --- /dev/null +++ b/test/server/flight-revalidate-all.spec.tsx @@ -0,0 +1,26 @@ +// `revalidate: "*"` reaches the collector as `revalidateKeys: ["*"]` and must collect every query. +import { query } from "../../src/data/query.js"; +import { createFlightDataCollector } from "../../src/server.js"; + +const getNotes = query(async () => ["note-1"], "revalidateAllNotes"); + +test("revalidate '*' collects every query on the page", async () => { + const collect = createFlightDataCollector({ + routes: [{ path: "/notes", preload: () => getNotes() }] + }); + const headers = new Headers({ referer: "http://localhost:3000/notes" }); + const request = new Request("http://localhost:3000/_server", { method: "POST", headers }); + const event = { request, response: { headers: new Headers() }, locals: {} }; + const outcome = { + id: "fn#0", + value: "mutated", + response: new Response(null, { headers: { "X-Revalidate": "*" } }), + request, + thrown: false, + targetUrl: "http://localhost:3000/notes", + revalidateKeys: ["*"], + foldedHeaders: headers + }; + const data: any = await collect(event as any, outcome as any); + expect(data && (await data["revalidateAllNotes[]"])).toEqual(["note-1"]); +});