Skip to content
Merged
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
8 changes: 8 additions & 0 deletions .changeset/chilly-needles-taste.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"react-router": patch
---

Update Lazy Route Discovery manifest requests to use a singular comma-separated `paths` query param instead of repeated `p` query params

- This is because Cloudflare has a hard limit of 100 URL search param key/value pairs when used as a key for caching purposes
- If more that 100 paths were included, the cache key would be incomplete and could produce false-positive cache hits
1 change: 1 addition & 0 deletions contributors.yml
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@
- johnpangalos
- jonkoops
- joseph0926
- jplhomer
- jrakotoharisoa
- jrestall
- juanpprieto
Expand Down
30 changes: 15 additions & 15 deletions integration/fog-of-war-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -715,7 +715,7 @@ test.describe("Fog of War", () => {
expect(await app.getHtml("#parent")).toMatch(`Parent`);
expect(await app.getHtml("#child2")).toMatch(`Child 2`);
expect(manifestRequests).toEqual([
expect.stringMatching(/\/__manifest\?p=%2Fparent%2Fchild2&version=/),
expect.stringMatching(/\/__manifest\?paths=%2Fparent%2Fchild2&version=/),
]);
});

Expand Down Expand Up @@ -783,7 +783,7 @@ test.describe("Fog of War", () => {
),
).toEqual(["root", "routes/_index", "routes/$slug"]);
expect(manifestRequests).toEqual([
expect.stringMatching(/\/__manifest\?p=%2Fsomething&version=/),
expect.stringMatching(/\/__manifest\?paths=%2Fsomething&version=/),
]);
manifestRequests = [];

Expand All @@ -797,7 +797,7 @@ test.describe("Fog of War", () => {
await page.waitForSelector("#static");
expect(await app.getHtml("#static")).toMatch("Static");
expect(manifestRequests).toEqual([
expect.stringMatching(/\/__manifest\?p=%2Fstatic&version=/),
expect.stringMatching(/\/__manifest\?paths=%2Fstatic&version=/),
]);
expect(
await page.evaluate(() =>
Expand Down Expand Up @@ -870,7 +870,7 @@ test.describe("Fog of War", () => {
),
).toEqual(["root", "routes/_index", "routes/$"]);
expect(manifestRequests).toEqual([
expect.stringMatching(/\/__manifest\?p=%2Fsomething&version=/),
expect.stringMatching(/\/__manifest\?paths=%2Fsomething&version=/),
]);
manifestRequests = [];

Expand All @@ -884,7 +884,7 @@ test.describe("Fog of War", () => {
await page.waitForSelector("#static");
expect(await app.getHtml("#static")).toMatch("Static");
expect(manifestRequests).toEqual([
expect.stringMatching(/\/__manifest\?p=%2Fstatic&version=/),
expect.stringMatching(/\/__manifest\?paths=%2Fstatic&version=/),
]);
expect(
await page.evaluate(() =>
Expand Down Expand Up @@ -956,7 +956,7 @@ test.describe("Fog of War", () => {
await page.waitForSelector("#slug");
expect(await app.getHtml("#slug")).toMatch("Slug: a");
expect(manifestRequests).toEqual([
expect.stringMatching(/\/__manifest\?p=%2Fa&version=/),
expect.stringMatching(/\/__manifest\?paths=%2Fa&version=/),
]);
manifestRequests = [];

Expand All @@ -977,7 +977,7 @@ test.describe("Fog of War", () => {
await page.waitForSelector("#slug");
expect(await app.getHtml("#slug")).toMatch("Slug: b");
expect(manifestRequests).toEqual([
expect.stringMatching(/\/__manifest\?p=%2Fb&version=/),
expect.stringMatching(/\/__manifest\?paths=%2Fb&version=/),
]);
});

Expand Down Expand Up @@ -1044,7 +1044,7 @@ test.describe("Fog of War", () => {
await page.waitForSelector("#splat");
expect(await app.getHtml("#splat")).toMatch("Splat: a");
expect(manifestRequests).toEqual([
expect.stringMatching(/\/__manifest\?p=%2Fa&version=/),
expect.stringMatching(/\/__manifest\?paths=%2Fa&version=/),
]);
manifestRequests = [];

Expand All @@ -1065,7 +1065,7 @@ test.describe("Fog of War", () => {
await page.waitForSelector("#splat");
expect(await app.getHtml("#splat")).toMatch("Splat: b/c");
expect(manifestRequests).toEqual([
expect.stringMatching(/\/__manifest\?p=%2Fb%2Fc&version=/),
expect.stringMatching(/\/__manifest\?paths=%2Fb%2Fc&version=/),
]);
});

Expand Down Expand Up @@ -1137,15 +1137,15 @@ test.describe("Fog of War", () => {
await app.clickLink("/not/a/path");
await page.waitForSelector("#error");
expect(manifestRequests).toEqual([
expect.stringMatching(/\/__manifest\?p=%2Fnot%2Fa%2Fpath&version=/),
expect.stringMatching(/\/__manifest\?paths=%2Fnot%2Fa%2Fpath&version=/),
]);
manifestRequests = [];

// Go to a valid slug route
await app.clickLink("/something");
await page.waitForSelector("#slug");
expect(manifestRequests).toEqual([
expect.stringMatching(/\/__manifest\?p=%2Fsomething&version=/),
expect.stringMatching(/\/__manifest\?paths=%2Fsomething&version=/),
]);
manifestRequests = [];

Expand Down Expand Up @@ -1233,7 +1233,7 @@ test.describe("Fog of War", () => {
await new Promise((resolve) => setTimeout(resolve, 250));
expect(manifestRequests).toEqual([
expect.stringMatching(
/\/__manifest\?p=%2F&p=%2Fa&p=%2Fb&version=[a-z0-9]{8}/,
/\/__manifest\?paths=%2F%2C%2Fa%2C%2Fb&version=[a-z0-9]{8}/,
),
]);
});
Expand Down Expand Up @@ -1275,7 +1275,7 @@ test.describe("Fog of War", () => {
await new Promise((resolve) => setTimeout(resolve, 250));
expect(manifestRequests).toEqual([
expect.stringMatching(
/\/__manifest\?p=%2F&p=%2Fa&p=%2Fb&p=%2Fc&p=%2Fd&p=%2Fe&p=%2Ff&p=%2F/,
/\/__manifest\?paths=%2F%2C%2Fa%2C%2Fb%2C%2Fc%2C%2Fd%2C%2Fe%2C%2Ff%2C%2Fg/,
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like the existing test was missing the g at the end for the final /g path

),
]);
});
Expand Down Expand Up @@ -1439,7 +1439,7 @@ test.describe("Fog of War", () => {
),
).toEqual(["root", "routes/_index", "routes/a"]);
expect(manifestRequests).toEqual([
expect.stringMatching(/\/custom-manifest\?p=%2F&p=%2Fa&version=/),
expect.stringMatching(/\/custom-manifest\?paths=%2F%2C%2Fa&version=/),
]);
manifestRequests = [];

Expand All @@ -1449,7 +1449,7 @@ test.describe("Fog of War", () => {
// Wait for eager discovery to kick off
await new Promise((r) => setTimeout(r, 500));
expect(manifestRequests).toEqual([
expect.stringMatching(/\/custom-manifest\?p=%2Fa%2Fb&version=/),
expect.stringMatching(/\/custom-manifest\?paths=%2Fa%2Fb&version=/),
]);

expect(wrongManifestRequests).toEqual([]);
Expand Down
2 changes: 1 addition & 1 deletion packages/react-router/lib/dom/ssr/fog-of-war.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ export async function fetchAndApplyManifestPatches(
// https://issues.chromium.org/issues/331406951
// https://github.com/nodejs/node/issues/51518
const searchParams = new URLSearchParams();
paths.sort().forEach((path) => searchParams.append("p", path));
searchParams.set("paths", paths.sort().join(","));
searchParams.set("version", manifest.version);
let url = new URL(
getManifestPath(manifestPath, basename),
Expand Down
2 changes: 1 addition & 1 deletion packages/react-router/lib/rsc/browser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -976,7 +976,7 @@ function getManifestUrl(paths: string[]): URL | null {
"",
);
let url = new URL(`${basename}/.manifest`, window.location.origin);
paths.sort().forEach((path) => url.searchParams.append("p", path));
url.searchParams.set("paths", paths.sort().join(","));

return url;
}
Expand Down
6 changes: 3 additions & 3 deletions packages/react-router/lib/rsc/server.rsc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -472,9 +472,9 @@ async function generateManifestResponse(
temporaryReferences: unknown,
) {
let url = new URL(request.url);
let pathnameParams = url.searchParams.getAll("p");
let pathnames = pathnameParams.length
? pathnameParams
let pathParam = url.searchParams.get("paths");
let pathnames = pathParam
? pathParam.split(",").filter(Boolean)
: [url.pathname.replace(/\.manifest$/, "")];
let routeIds = new Set<string>();
let matchedRoutes = pathnames
Expand Down
6 changes: 4 additions & 2 deletions packages/react-router/lib/server-runtime/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ async function handleManifestRequest(

let patches: Record<string, EntryRoute> = {};

if (url.searchParams.has("p")) {
if (url.searchParams.has("paths")) {
let paths = new Set<string>();

// In addition to responding with the patches for the requested paths, we
Expand All @@ -365,7 +365,9 @@ async function handleManifestRequest(
// for client side matching if the user routes back up to `/parent`.
// This is the same thing we do on initial load in <Scripts> via
// `getPartialManifest()`
url.searchParams.getAll("p").forEach((path) => {
let pathParam = url.searchParams.get("paths") || "";
let requestedPaths = pathParam.split(",").filter(Boolean);
requestedPaths.forEach((path) => {
if (!path.startsWith("/")) {
path = `/${path}`;
}
Expand Down
Loading