fix(client): accept larger SSE events (#36442)
This commit is contained in:
parent
e3f7637eb2
commit
66b9cc7931
4 changed files with 48 additions and 4 deletions
|
|
@ -1,4 +1,9 @@
|
|||
export type ClientErrorReason = "Transport" | "UnexpectedStatus" | "UnsupportedContentType" | "MalformedResponse"
|
||||
export type ClientErrorReason =
|
||||
| "Transport"
|
||||
| "UnexpectedStatus"
|
||||
| "UnsupportedContentType"
|
||||
| "MalformedResponse"
|
||||
| "SseEventTooLarge"
|
||||
|
||||
export class ClientError extends Error {
|
||||
override readonly name = "ClientError"
|
||||
|
|
|
|||
|
|
@ -213,6 +213,8 @@ interface RequestDescriptor {
|
|||
readonly binary?: true
|
||||
}
|
||||
|
||||
const maxSseEventBytes = 16 * 1024 * 1024
|
||||
|
||||
export function make(options: ClientOptions) {
|
||||
const fetch = options.fetch ?? globalThis.fetch
|
||||
|
||||
|
|
@ -289,7 +291,7 @@ export function make(options: ClientOptions) {
|
|||
throw new ClientError("Transport", { cause })
|
||||
}
|
||||
buffer += decoder.decode(next.value, { stream: !next.done })
|
||||
if (buffer.length > 1_048_576) throw new ClientError("MalformedResponse")
|
||||
if (buffer.length > maxSseEventBytes) throw new ClientError("SseEventTooLarge")
|
||||
const trailingCarriageReturn = !next.done && buffer.endsWith("\r")
|
||||
if (trailingCarriageReturn) buffer = buffer.slice(0, -1)
|
||||
buffer = buffer.replaceAll("\r\n", "\n").replaceAll("\r", "\n")
|
||||
|
|
|
|||
|
|
@ -284,6 +284,43 @@ test("event.subscribe terminates on malformed Promise SSE data", async () => {
|
|||
})
|
||||
})
|
||||
|
||||
test("event.subscribe accepts a fragmented SSE event below the size limit", async () => {
|
||||
const event = { id: "evt_large", type: "test.large", data: { output: "x".repeat(12 * 1024 * 1024) } }
|
||||
const encoded = new TextEncoder().encode(`data: ${JSON.stringify(event)}\n\n`)
|
||||
const client = OpenCode.make({
|
||||
baseUrl: "http://localhost:3000",
|
||||
fetch: async () =>
|
||||
new Response(
|
||||
new ReadableStream({
|
||||
start(controller) {
|
||||
for (let offset = 0; offset < encoded.length; offset += 64 * 1024) {
|
||||
controller.enqueue(encoded.slice(offset, offset + 64 * 1024))
|
||||
}
|
||||
controller.close()
|
||||
},
|
||||
}),
|
||||
{ headers: { "content-type": "text/event-stream" } },
|
||||
),
|
||||
})
|
||||
|
||||
await expect(client.event.subscribe()[Symbol.asyncIterator]().next()).resolves.toEqual({ done: false, value: event })
|
||||
})
|
||||
|
||||
test("event.subscribe rejects an SSE event above the size limit", async () => {
|
||||
const client = OpenCode.make({
|
||||
baseUrl: "http://localhost:3000",
|
||||
fetch: async () =>
|
||||
new Response(`data: ${JSON.stringify({ output: "x".repeat(16 * 1024 * 1024) })}`, {
|
||||
headers: { "content-type": "text/event-stream" },
|
||||
}),
|
||||
})
|
||||
|
||||
await expect(client.event.subscribe()[Symbol.asyncIterator]().next()).rejects.toMatchObject({
|
||||
name: "ClientError",
|
||||
reason: "SseEventTooLarge",
|
||||
})
|
||||
})
|
||||
|
||||
test("session methods use the public HTTP contract", async () => {
|
||||
const requests: Array<{ url: string; init?: RequestInit }> = []
|
||||
const client = OpenCode.make({
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue