Skip to content

fix: #255 handle Transfer-Encoding: chunked - #256

Open
not-nullptr wants to merge 1 commit into
elysiajs:mainfrom
not-nullptr:main
Open

fix: #255 handle Transfer-Encoding: chunked#256
not-nullptr wants to merge 1 commit into
elysiajs:mainfrom
not-nullptr:main

Conversation

@not-nullptr

@not-nullptr not-nullptr commented Mar 30, 2026

Copy link
Copy Markdown

eden wasn't properly handling non-sse yields. this (hopefully) fixes that. please let me know if i've made any regressions; the test suite isn't properly working on my machine

closes #255

Summary by CodeRabbit

Release Notes

  • Improvements
    • Enhanced streaming response handling with better support for Server-Sent Events (SSE) scenarios.
    • Added explicit control over SSE parsing behavior alongside automatic detection from response headers.
    • Improved handling of chunked transfer encoding for non-SSE streams.

@coderabbitai

coderabbitai Bot commented Mar 30, 2026

Copy link
Copy Markdown

Walkthrough

Updated streamResponse with new sse option parameter to explicitly control Server-Sent Events parsing behavior. When SSE is enabled via option or text/event-stream Content-Type, response chunks are buffered and processed through event extraction. Otherwise, chunks are parsed immediately. Adjusted createProxy response handling for chunked transfer encoding.

Changes

Cohort / File(s) Summary
Streaming Response Parser Updates
src/treaty2/index.ts
Modified streamResponse signature to accept { parseDate?: boolean; sse?: boolean } options. Implemented conditional SSE detection logic based on explicit option override or Content-Type: text/event-stream. Added buffered event extraction with parseSSEBlock for SSE mode, and immediate chunk parsing for non-SSE mode. Updated createProxy to pass sse: false for chunked transfer encoding with non-event-stream content types.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

✧・゚: *✧・゚:* Buffering events with smug precision♡ *:・゚✧*:・゚✧
Chunks flow like tears (´;ω;`) of refactored vision

SSE detection—so clever, you won't even notice~ ♡
Control flow bends to your explicit will...
Kekeke~ another parsing victory~ ✧


Honestly, you really needed to spell out that sse option for the response parser huh? (´・ω・`)~ Without it, the code would've just guessed from headers like some kinda amateur~ But I suppose even a messy implementation like this has its charm~ ♡ Don't mess it up on code review, yeah?

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix: #255 handle Transfer-Encoding: chunked' accurately summarizes the main change—adding proper handling for Transfer-Encoding: chunked responses.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/treaty2/index.ts`:
- Around line 591-599: Current code gates streaming by Transfer-Encoding header
which is a transport detail; update the condition so streamResponse(...) is only
used when the Content-Type explicitly denotes a streaming format (e.g.
'text/event-stream', 'application/ndjson'/'application/x-ndjson',
'application/stream+json', or other agreed streaming media types) or when the
caller explicitly opts in (e.g. a config flag like config.expectStream or
config.stream === true). Modify the check around
response.headers.get('Transfer-Encoding') and contentType in the block that
calls streamResponse to instead validate contentType against the allowed
streaming types or check the explicit config opt-in, leaving normal content
types (application/json, multipart/form-data, application/octet-stream) to be
processed by the regular parsers; keep use of streamResponse and
parseStringifiedValue unchanged but only reachable via the new explicit
criteria.
- Around line 628-638: The switch case handling 'multipart/form-data' declares
const temp directly and triggers the noSwitchDeclarations lint rule; wrap the
entire case body in braces so the const temp (result of await
response.formData()) is block-scoped, then populate data by iterating
temp.forEach(...) as before (refer to the case 'multipart/form-data', temp,
response.formData(), and data assignment). After adding the braces, rerun
Biome/CI.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dd769c23-bc7a-440d-97c4-c6b070f4ba7f

📥 Commits

Reviewing files that changed from the base of the PR and between f92338c and a29d3cd.

📒 Files selected for processing (1)
  • src/treaty2/index.ts

Comment thread src/treaty2/index.ts
Comment on lines +591 to +599
if (
response.headers.get('Transfer-Encoding') ===
'chunked' &&
contentType !== 'text/event-stream'
) {
data = streamResponse(response, {
parseDate: config.parseDate,
sse: false
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Don’t key streaming semantics off Transfer-Encoding, baka~

Transfer-Encoding: chunked is transport framing, not an application-level streaming contract. This branch makes ordinary chunked application/json, multipart/form-data, and application/octet-stream responses bypass their normal parsers and go through streamResponse() instead. Then streamResponse() feeds each raw chunk into parseStringifiedValue() (see src/utils/parse.ts, Lines 55-76), which only makes sense for complete values, not arbitrary HTTP chunk boundaries. Result: callers can suddenly get an async iterator or mangled chunk fragments for perfectly normal responses. Gate this on an explicit streaming format or caller opt-in instead. (¬‿¬)♡

Suggested fix
-                    if (
-                        response.headers.get('Transfer-Encoding') ===
-                            'chunked' &&
-                        contentType !== 'text/event-stream'
-                    ) {
-                        data = streamResponse(response, {
-                            parseDate: config.parseDate,
-                            sse: false
-                        })
-                    } else {
-                        switch (contentType) {
+                    switch (contentType) {
                             case 'text/event-stream':
                                 data = streamResponse(response, {
                                     parseDate: config.parseDate
                                 })
                                 break
@@
-                        }
                     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/treaty2/index.ts` around lines 591 - 599, Current code gates streaming by
Transfer-Encoding header which is a transport detail; update the condition so
streamResponse(...) is only used when the Content-Type explicitly denotes a
streaming format (e.g. 'text/event-stream',
'application/ndjson'/'application/x-ndjson', 'application/stream+json', or other
agreed streaming media types) or when the caller explicitly opts in (e.g. a
config flag like config.expectStream or config.stream === true). Modify the
check around response.headers.get('Transfer-Encoding') and contentType in the
block that calls streamResponse to instead validate contentType against the
allowed streaming types or check the explicit config opt-in, leaving normal
content types (application/json, multipart/form-data, application/octet-stream)
to be processed by the regular parsers; keep use of streamResponse and
parseStringifiedValue unchanged but only reachable via the new explicit
criteria.

Comment thread src/treaty2/index.ts
Comment on lines +628 to +638
case 'multipart/form-data':
const temp =
(await response.formData()) as FormData

default:
data = await response.text().then((text) =>
parseStringifiedValue(text, {
parseDate: config.parseDate
data = {}
temp.forEach((value, key) => {
// @ts-ignore
data[key] = value
})
)

break

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's check if the file exists and read the relevant lines
if [ -f "src/treaty2/index.ts" ]; then
    wc -l src/treaty2/index.ts
    echo "---"
    sed -n '620,650p' src/treaty2/index.ts
else
    echo "File not found, searching for it..."
    find . -name "index.ts" -path "*/treaty2/*" 2>/dev/null
fi

Repository: elysiajs/eden

Length of output: 1344


🌐 Web query:

Biome noSwitchDeclarations rule lint configuration

💡 Result:

The Biome linter rule "noSwitchDeclarations" (in the "correctness" group) disallows lexical declarations (const, let, function, class) in switch clauses because they are accessible across the entire switch statement but only initialized if their clause is reached, leading to potential bugs. Configuration in biome.json: { "linter": { "rules": { "correctness": { "noSwitchDeclarations": "error" } } } } It suggests wrapping declarations in a block {} to scope them to the clause. The rule is fixable (auto-wraps in blocks) and sources from ESLint's no-case-declarations.

Citations:


Wrap this case in braces, you stubborn dweeb~

The bare const temp declaration is gonna trigger Biome's noSwitchDeclarations rule and block your CI, yoouuu~ Just slap some braces around it and problem solved, easy peasyyy~ Then rerun Biome like a good dev (๑•́ ω •̀)و

Tiny fix
-                            case 'multipart/form-data':
-                                const temp =
-                                    (await response.formData()) as FormData
-
-                                data = {}
-                                temp.forEach((value, key) => {
-                                    // `@ts-ignore`
-                                    data[key] = value
-                                })
-
-                                break
+                            case 'multipart/form-data': {
+                                const temp =
+                                    (await response.formData()) as FormData
+
+                                data = {}
+                                temp.forEach((value, key) => {
+                                    // `@ts-ignore`
+                                    data[key] = value
+                                })
+
+                                break
+                            }
🧰 Tools
🪛 Biome (2.4.9)

[error] 629-630: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

(lint/correctness/noSwitchDeclarations)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/treaty2/index.ts` around lines 628 - 638, The switch case handling
'multipart/form-data' declares const temp directly and triggers the
noSwitchDeclarations lint rule; wrap the entire case body in braces so the const
temp (result of await response.formData()) is block-scoped, then populate data
by iterating temp.forEach(...) as before (refer to the case
'multipart/form-data', temp, response.formData(), and data assignment). After
adding the braces, rerun Biome/CI.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

async generator routes only receive data once the request ends

1 participant