From 826c8261afaadf4e85e3b65c7c79437f8a058d86 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Mon, 8 Jul 2024 08:59:43 -0700 Subject: [PATCH 1/4] chore(astro): optimize stream modification with byte-level processing --- packages/astro/src/server/clerk-middleware.ts | 73 ++++++++++--------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/packages/astro/src/server/clerk-middleware.ts b/packages/astro/src/server/clerk-middleware.ts index 753df636cd8..379d18c08bc 100644 --- a/packages/astro/src/server/clerk-middleware.ts +++ b/packages/astro/src/server/clerk-middleware.ts @@ -228,6 +228,16 @@ function decorateAstroLocal(req: Request, context: APIContext, requestState: Req context.locals.currentUser = createCurrentUser(req, context); } +/** + * Find the index of the closing head tag in the chunk. + * + * Note: This implementation uses a simple approach that works for most of our + * current use cases. + */ +function findClosingHeadTagIndex(chunk: Uint8Array, endHeadTag: Uint8Array) { + return chunk.findIndex((_, i) => endHeadTag.every((value, j) => value === chunk[i + j])); +} + async function decorateRequest( locals: APIContext['locals'], res: Response, @@ -245,48 +255,39 @@ async function decorateRequest( * without sucrificing DX and having developers wrap each page with a Layout that would handle this. */ if (res.headers.get('content-type') === 'text/html') { - const reader = res.body?.getReader(); - const stream = new ReadableStream({ - async start(controller) { - let { value, done } = await reader!.read(); - const encoder = new TextEncoder(); - const decoder = new TextDecoder(); - while (!done) { - const decodedValue = decoder.decode(value); - - /** - * Hijack html response to position `__CLERK_ASTRO_DATA__` before the closing `head` html tag - */ - if (decodedValue.includes('')) { - const [p1, p2] = decodedValue.split(''); - controller.enqueue(encoder.encode(p1)); - controller.enqueue( - encoder.encode( - `\n`, - ), - ); - - controller.enqueue( - encoder.encode( - `\n`, - ), - ); + const encoder = new TextEncoder(); + const closingHeadTag = encoder.encode(''); + const clerkAstroData = encoder.encode( + `\n`, + ); + const clerkSafeEnvVariables = encoder.encode( + `\n`, + ); + const hotloadScript = encoder.encode(buildClerkHotloadScript(locals)); + + const stream = res.body!.pipeThrough( + new TransformStream({ + transform(chunk, controller) { + const index = findClosingHeadTagIndex(chunk, closingHeadTag); + const isClosingHeadTagFound = index !== -1; + + if (isClosingHeadTagFound) { + controller.enqueue(chunk.slice(0, index)); + controller.enqueue(clerkAstroData); + controller.enqueue(clerkSafeEnvVariables); if (__HOTLOAD__) { - controller.enqueue(encoder.encode(buildClerkHotloadScript(locals))); + controller.enqueue(hotloadScript); } - controller.enqueue(encoder.encode('')); - controller.enqueue(encoder.encode(p2)); + controller.enqueue(closingHeadTag); + controller.enqueue(chunk.slice(index + closingHeadTag.length)); } else { - controller.enqueue(value); + controller.enqueue(chunk); } - - ({ value, done } = await reader!.read()); - } - controller.close(); - }, - }); + }, + }), + ); const modifiedResponse = new Response(stream, { status: res.status, From 61be6f58e368c28b892c11b972367d3fd8ada355 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Mon, 8 Jul 2024 09:03:42 -0700 Subject: [PATCH 2/4] chore(astro): add comment regarding positioning of script tag --- packages/astro/src/server/clerk-middleware.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/astro/src/server/clerk-middleware.ts b/packages/astro/src/server/clerk-middleware.ts index 379d18c08bc..be08f6642af 100644 --- a/packages/astro/src/server/clerk-middleware.ts +++ b/packages/astro/src/server/clerk-middleware.ts @@ -271,6 +271,9 @@ async function decorateRequest( const index = findClosingHeadTagIndex(chunk, closingHeadTag); const isClosingHeadTagFound = index !== -1; + /** + * Hijack html response to position `__CLERK_ASTRO_DATA__` before the closing `head` html tag + */ if (isClosingHeadTagFound) { controller.enqueue(chunk.slice(0, index)); controller.enqueue(clerkAstroData); From cc675c3784a2c47ee58754a3e5a94e022c9db1d3 Mon Sep 17 00:00:00 2001 From: Robert Soriano Date: Mon, 8 Jul 2024 09:10:58 -0700 Subject: [PATCH 3/4] Create olive-foxes-own.md --- .changeset/olive-foxes-own.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/olive-foxes-own.md diff --git a/.changeset/olive-foxes-own.md b/.changeset/olive-foxes-own.md new file mode 100644 index 00000000000..887d2b5023b --- /dev/null +++ b/.changeset/olive-foxes-own.md @@ -0,0 +1,5 @@ +--- +"@clerk/astro": patch +--- + +Introduce `TransformStream` and work directly with encoded chunks when modifiying stream From 8557e8fe97bcb3776649b4e15658e903e5f10a51 Mon Sep 17 00:00:00 2001 From: Robert Soriano Date: Mon, 8 Jul 2024 09:52:33 -0700 Subject: [PATCH 4/4] Update changeset message --- .changeset/olive-foxes-own.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/olive-foxes-own.md b/.changeset/olive-foxes-own.md index 887d2b5023b..5d411d8e05a 100644 --- a/.changeset/olive-foxes-own.md +++ b/.changeset/olive-foxes-own.md @@ -2,4 +2,4 @@ "@clerk/astro": patch --- -Introduce `TransformStream` and work directly with encoded chunks when modifiying stream +Improve stream processing performance