From 95d6cb887d5ec5f9eb4b6d97a8532b87234d59b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=A2=D0=B0=D1=80?= =?UTF-8?q?=D0=B0=D1=81=D0=BE=D0=B2?= Date: Fri, 15 Nov 2024 01:08:41 +0300 Subject: [PATCH 1/5] fix(request-state): fix data race MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch fixes condition where client already hydated and expects server data from naostores but nanostores not populated with server data yet Signed-off-by: Максим Тарасов --- packages/request-state/src/runtime/middleware.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/request-state/src/runtime/middleware.ts b/packages/request-state/src/runtime/middleware.ts index 1856eb2b..49ae434d 100644 --- a/packages/request-state/src/runtime/middleware.ts +++ b/packages/request-state/src/runtime/middleware.ts @@ -26,13 +26,13 @@ function injectState(getState: () => string | false) { return new TransformStream({ transform(chunk, controller) { if (!injected) { - const bodyCloseIndex = chunk.indexOf(''); - if (bodyCloseIndex > -1) { + const headCloseIndex = chunk.indexOf(''); + if (headCloseIndex > -1) { const state = getState(); if (state) { const stateScript = ``; - chunk = chunk.slice(0, bodyCloseIndex) + stateScript + chunk.slice(bodyCloseIndex); + chunk = chunk.slice(0, headCloseIndex) + stateScript + chunk.slice(headCloseIndex); } injected = true; } From 00086dcf2929fbe4d0d984149d64ded86bc220d7 Mon Sep 17 00:00:00 2001 From: MWGuy Date: Sat, 16 Nov 2024 17:26:47 +0300 Subject: [PATCH 2/5] docs: Describe inability to use response streaming while using request-state and request-nanostores packages --- docs/src/content/docs/request-nanostores.mdx | 4 ++++ docs/src/content/docs/request-state.mdx | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/docs/src/content/docs/request-nanostores.mdx b/docs/src/content/docs/request-nanostores.mdx index 7011ed08..b5bc0176 100644 --- a/docs/src/content/docs/request-nanostores.mdx +++ b/docs/src/content/docs/request-nanostores.mdx @@ -117,6 +117,10 @@ $comments.set(await loadCommentsForArticle()); ``` +## Caveats + +Enabling request-nanostores disables [response streaming](https://docs.astro.build/en/recipes/streaming-improve-page-performance/). This behavior caused by [request-state](/request-state) package that prevents race condition where nanostores not initialized before client components starts hydrating. + ## License Request Nanostores is available under the MIT license. diff --git a/docs/src/content/docs/request-state.mdx b/docs/src/content/docs/request-state.mdx index c24ee537..7831a190 100644 --- a/docs/src/content/docs/request-state.mdx +++ b/docs/src/content/docs/request-state.mdx @@ -130,6 +130,10 @@ document.addEventListener('@it-astro:server-state-loaded', (event) => { }); ``` +## Caveats + +Enabling request-state disables [response streaming](https://docs.astro.build/en/recipes/streaming-improve-page-performance/). The entire application state must be generated before shipping closing head tag to the client. This behavior prevents data race when client components start hydrating before entire document is loaded. + ## License Astro Request State is available under the MIT license. From c072e5b82a1c0c22363c5f5568a9003a6c32051b Mon Sep 17 00:00:00 2001 From: Luiz Ferraz Date: Mon, 9 Dec 2024 22:29:19 -0300 Subject: [PATCH 3/5] Disable streaming --- .../request-state/src/runtime/middleware.ts | 43 +++++++------------ 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/packages/request-state/src/runtime/middleware.ts b/packages/request-state/src/runtime/middleware.ts index 49ae434d..cdaf3265 100644 --- a/packages/request-state/src/runtime/middleware.ts +++ b/packages/request-state/src/runtime/middleware.ts @@ -13,31 +13,20 @@ export const onRequest = defineMiddleware(async (_, next) => { if (mediaType !== 'text/html' && !mediaType.startsWith('text/html+')) return result; - const newBody = result.body - ?.pipeThrough(new TextDecoderStream()) - .pipeThrough(injectState(getState)) - .pipeThrough(new TextEncoderStream()); - - return new Response(newBody, result); + const originalBody = await result.text(); + + const headCloseIndex = originalBody.indexOf(''); + if (headCloseIndex > -1) { + const state = getState(); + if (state) { + const stateScript = ``; + + return new Response( + originalBody.slice(0, headCloseIndex) + stateScript + originalBody.slice(headCloseIndex), + result + ); + } + } + + return new Response(originalBody, result); }); - -function injectState(getState: () => string | false) { - let injected = false; - return new TransformStream({ - transform(chunk, controller) { - if (!injected) { - const headCloseIndex = chunk.indexOf(''); - if (headCloseIndex > -1) { - const state = getState(); - if (state) { - const stateScript = ``; - - chunk = chunk.slice(0, headCloseIndex) + stateScript + chunk.slice(headCloseIndex); - } - injected = true; - } - } - controller.enqueue(chunk); - }, - }); -} From 66e7585312dd1d8a83450cd53cf212bca7812858 Mon Sep 17 00:00:00 2001 From: Luiz Ferraz Date: Mon, 9 Dec 2024 22:30:38 -0300 Subject: [PATCH 4/5] Add changeset --- .changeset/neat-panthers-notice.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/neat-panthers-notice.md diff --git a/.changeset/neat-panthers-notice.md b/.changeset/neat-panthers-notice.md new file mode 100644 index 00000000..548c986c --- /dev/null +++ b/.changeset/neat-panthers-notice.md @@ -0,0 +1,5 @@ +--- +'@inox-tools/request-state': minor +--- + +Fixes flash of page with unsynchronized state due to response streaming. From 372d2301169c71054c170ce9de690118ffa53ac7 Mon Sep 17 00:00:00 2001 From: Luiz Ferraz Date: Mon, 9 Dec 2024 22:35:32 -0300 Subject: [PATCH 5/5] Update docs --- docs/src/content/docs/request-nanostores.mdx | 2 +- docs/src/content/docs/request-state.mdx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/content/docs/request-nanostores.mdx b/docs/src/content/docs/request-nanostores.mdx index 3312677c..ddd7731b 100644 --- a/docs/src/content/docs/request-nanostores.mdx +++ b/docs/src/content/docs/request-nanostores.mdx @@ -119,7 +119,7 @@ $comments.set(await loadCommentsForArticle()); ## Caveats -Enabling request-nanostores disables [response streaming](https://docs.astro.build/en/recipes/streaming-improve-page-performance/). This behavior caused by [request-state](/request-state) package that prevents race condition where nanostores not initialized before client components starts hydrating. +Enabling Request-Scoped Nanostores disables [response streaming](https://docs.astro.build/en/recipes/streaming-improve-page-performance/). This behavior caused by [request-state](/request-state) package that prevents race condition where nanostores not initialized before client components starts hydrating. ## License diff --git a/docs/src/content/docs/request-state.mdx b/docs/src/content/docs/request-state.mdx index 206063b9..1e23a43b 100644 --- a/docs/src/content/docs/request-state.mdx +++ b/docs/src/content/docs/request-state.mdx @@ -132,7 +132,7 @@ document.addEventListener('@it-astro:server-state-loaded', (event) => { ## Caveats -Enabling request-state disables [response streaming](https://docs.astro.build/en/recipes/streaming-improve-page-performance/). The entire application state must be generated before shipping closing head tag to the client. This behavior prevents data race when client components start hydrating before entire document is loaded. +Enabling Request State disables [response streaming](https://docs.astro.build/en/recipes/streaming-improve-page-performance/). The entire application state must be generated before shipping closing head tag to the client. This behavior prevents data race when client components start hydrating before entire document is loaded. ## License