Skip to content
Open
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
50 changes: 50 additions & 0 deletions src/adapter/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,56 @@ export const createStreamHandler =
}
}

// Plain ReadableStream (not SSE, not a generator) can be passed through
// directly. Re-wrapping it would add per-chunk async overhead (#1741)
// and break the native cancel callback (#1768).
if (
!isSSE &&
generator instanceof ReadableStream &&
typeof (generator as any).next !== 'function'
)
return new Response(
generator.pipeThrough(
new TransformStream({
transform(chunk, controller) {
if (
chunk instanceof Uint8Array ||
chunk instanceof ArrayBuffer ||
typeof chunk === 'string'
)
controller.enqueue(chunk)
else if (ArrayBuffer.isView(chunk))
controller.enqueue(
new Uint8Array(
chunk.buffer,
chunk.byteOffset,
chunk.byteLength
)
)
else if (chunk instanceof Blob)
return chunk
.arrayBuffer()
.then((buf) =>
controller.enqueue(
new Uint8Array(buf)
)
)
else
try {
controller.enqueue(
typeof chunk === 'object'
? JSON.stringify(chunk)
: String(chunk)
)
} catch {
controller.enqueue(String(chunk))
}
}
})
),
set as any
)

// Get an explicit async iterator so pull() can advance one step at a time.
// Generators already implement the iterator protocol directly (.next()),
// while ReadableStream (which generator may be reassigned to above) needs
Expand Down