From 2c612b7e882000948c0d3f228cde958cd267b6e8 Mon Sep 17 00:00:00 2001 From: Nicolas Dorseuil Date: Mon, 19 May 2025 10:26:28 +0200 Subject: [PATCH 1/2] decode path params in cache interceptor --- .changeset/fast-clouds-shout.md | 5 ++++ .../src/core/routing/cacheInterceptor.ts | 25 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 .changeset/fast-clouds-shout.md diff --git a/.changeset/fast-clouds-shout.md b/.changeset/fast-clouds-shout.md new file mode 100644 index 00000000..2cc6a41e --- /dev/null +++ b/.changeset/fast-clouds-shout.md @@ -0,0 +1,5 @@ +--- +"@opennextjs/aws": patch +--- + +decode path params in cache interceptor diff --git a/packages/open-next/src/core/routing/cacheInterceptor.ts b/packages/open-next/src/core/routing/cacheInterceptor.ts index cf441331..d92e77ae 100644 --- a/packages/open-next/src/core/routing/cacheInterceptor.ts +++ b/packages/open-next/src/core/routing/cacheInterceptor.ts @@ -129,6 +129,28 @@ async function generateResult( }, }; } +/** + * + * SSG cache key needs to be decoded, but some characters needs to be properly escaped + * https://github.com/vercel/next.js/blob/34039551d2e5f611c0abde31a197d9985918adaf/packages/next/src/server/lib/router-utils/decode-path-params.ts#L11-L26 + */ +function decodePathParams(pathname: string): string { + return pathname + .split("/") + .map((segment) => { + try { + // https://github.com/vercel/next.js/blob/34039551d2e5f611c0abde31a197d9985918adaf/packages/next/src/shared/lib/router/utils/escape-path-delimiters.ts#L2-L10 + return decodeURIComponent(segment).replace( + /([\/#?]|%(2f|23|3f|5c))/gi, + (char: string) => encodeURIComponent(char), + ); + } catch (e) { + // If decodeURIComponent fails, we return the original segment + return segment; + } + }) + .join("/"); +} export async function cacheInterceptor( event: InternalEvent, @@ -147,6 +169,9 @@ export async function cacheInterceptor( // We also need to remove trailing slash localizedPath = localizedPath.replace(/\/$/, ""); + // Then we decode the path params + localizedPath = decodePathParams(localizedPath); + debug("Checking cache for", localizedPath, PrerenderManifest); const isISR = From e89a0d138cc24526fe1eb419e21c4bbb47acb044 Mon Sep 17 00:00:00 2001 From: Nicolas Dorseuil Date: Mon, 19 May 2025 16:52:07 +0200 Subject: [PATCH 2/2] review fix --- .../src/core/routing/cacheInterceptor.ts | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/packages/open-next/src/core/routing/cacheInterceptor.ts b/packages/open-next/src/core/routing/cacheInterceptor.ts index d92e77ae..8f34f0ae 100644 --- a/packages/open-next/src/core/routing/cacheInterceptor.ts +++ b/packages/open-next/src/core/routing/cacheInterceptor.ts @@ -129,6 +129,21 @@ async function generateResult( }, }; } + +/** + * + * https://github.com/vercel/next.js/blob/34039551d2e5f611c0abde31a197d9985918adaf/packages/next/src/shared/lib/router/utils/escape-path-delimiters.ts#L2-L10 + */ +function escapePathDelimiters( + segment: string, + escapeEncoded?: boolean, +): string { + return segment.replace( + new RegExp(`([/#?]${escapeEncoded ? "|%(2f|23|3f|5c)" : ""})`, "gi"), + (char: string) => encodeURIComponent(char), + ); +} + /** * * SSG cache key needs to be decoded, but some characters needs to be properly escaped @@ -139,11 +154,7 @@ function decodePathParams(pathname: string): string { .split("/") .map((segment) => { try { - // https://github.com/vercel/next.js/blob/34039551d2e5f611c0abde31a197d9985918adaf/packages/next/src/shared/lib/router/utils/escape-path-delimiters.ts#L2-L10 - return decodeURIComponent(segment).replace( - /([\/#?]|%(2f|23|3f|5c))/gi, - (char: string) => encodeURIComponent(char), - ); + return escapePathDelimiters(decodeURIComponent(segment), true); } catch (e) { // If decodeURIComponent fails, we return the original segment return segment;